mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-24 02:04:04 +08:00
1814 lines
65 KiB
Python
1814 lines
65 KiB
Python
import os
|
|
import tempfile
|
|
|
|
import pytest
|
|
|
|
from galaxy.tool_util.lint import (
|
|
lint_tool_source_with,
|
|
lint_xml_with,
|
|
LintContext,
|
|
XMLLintMessageLine,
|
|
XMLLintMessageXPath,
|
|
)
|
|
from galaxy.tool_util.linters import (
|
|
citations,
|
|
command,
|
|
general,
|
|
help,
|
|
inputs,
|
|
outputs,
|
|
stdio,
|
|
tests,
|
|
xml_order,
|
|
)
|
|
from galaxy.tool_util.loader_directory import load_tool_sources_from_path
|
|
from galaxy.tool_util.parser.xml import XmlToolSource
|
|
from galaxy.util import parse_xml
|
|
from galaxy.util.xml_macros import load_with_references
|
|
|
|
# TODO tests tool xml for general linter
|
|
# tests tool xml for citations linter
|
|
CITATIONS_MULTIPLE = """
|
|
<tool>
|
|
<citations/>
|
|
<citations/>
|
|
</tool>
|
|
"""
|
|
|
|
CITATIONS_ABSENT = """
|
|
<tool/>
|
|
"""
|
|
|
|
CITATIONS_ERRORS = """
|
|
<tool>
|
|
<citations>
|
|
<nonsense/>
|
|
<citation type="hoerensagen"/>
|
|
<citation type="doi"> </citation>
|
|
</citations>
|
|
</tool>
|
|
"""
|
|
|
|
CITATIONS_VALID = """
|
|
<tool>
|
|
<citations>
|
|
<citation type="doi">DOI</citation>
|
|
</citations>
|
|
</tool>
|
|
"""
|
|
|
|
# tests tool xml for command linter
|
|
COMMAND_MULTIPLE = """
|
|
<tool>
|
|
<command/>
|
|
<command/>
|
|
</tool>
|
|
"""
|
|
COMMAND_MISSING = """
|
|
<tool/>
|
|
"""
|
|
COMMAND_TODO = """
|
|
<tool>
|
|
<command>
|
|
## TODO
|
|
</command>
|
|
</tool>
|
|
"""
|
|
COMMAND_DETECT_ERRORS_INTERPRETER = """
|
|
<tool>
|
|
<command detect_errors="nonsense" interpreter="python"/>
|
|
</tool>
|
|
"""
|
|
|
|
|
|
# tests tool xml for general linter
|
|
GENERAL_MISSING_TOOL_ID_NAME_VERSION = """
|
|
<tool profile="2109">
|
|
</tool>
|
|
"""
|
|
|
|
GENERAL_WHITESPACE_IN_VERSIONS_AND_NAMES = """
|
|
<tool name=" BWA Mapper " id="bwa tool" version=" 1.0.1 " is_multi_byte="true" display_interface="true" require_login="true" hidden="true">
|
|
<requirements>
|
|
<requirement type="package" version=" 1.2.5 "> bwa </requirement>
|
|
</requirements>
|
|
</tool>
|
|
"""
|
|
|
|
GENERAL_REQUIREMENT_WO_VERSION = """
|
|
<tool name="BWA Mapper" id="bwa_tool" version="1.0.1blah" is_multi_byte="true" display_interface="true" require_login="true" hidden="true" profile="20.09">
|
|
<requirements>
|
|
<requirement type="package">bwa</requirement>
|
|
<requirement type="package" version="1.2.5"></requirement>
|
|
</requirements>
|
|
</tool>
|
|
"""
|
|
|
|
GENERAL_VALID = """
|
|
<tool name="valid name" id="valid_id" version="1.0+galaxy1" profile="21.09">
|
|
</tool>
|
|
"""
|
|
|
|
# test tool xml for help linter
|
|
HELP_MULTIPLE = """
|
|
<tool>
|
|
<help>Help</help>
|
|
<help>More help</help>
|
|
</tool>
|
|
"""
|
|
|
|
HELP_ABSENT = """
|
|
<tool>
|
|
</tool>
|
|
"""
|
|
|
|
HELP_EMPTY = """
|
|
<tool>
|
|
<help> </help>
|
|
</tool>
|
|
"""
|
|
|
|
HELP_TODO = """
|
|
<tool>
|
|
<help>TODO</help>
|
|
</tool>
|
|
"""
|
|
|
|
HELP_INVALID_RST = """
|
|
<tool>
|
|
<help>
|
|
**xxl__
|
|
</help>
|
|
</tool>
|
|
"""
|
|
|
|
# test tool xml for inputs linter
|
|
INPUTS_NO_INPUTS = """
|
|
<tool>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_NO_INPUTS_DATASOURCE = """
|
|
<tool tool_type="data_source">
|
|
<inputs/>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_VALID = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="txt_param" type="text"/>
|
|
<param name="int_param" type="integer"/>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_PARAM_NAME = """
|
|
<tool>
|
|
<inputs>
|
|
<param type="text"/>
|
|
<param name="" type="text"/>
|
|
<param name="2" type="text"/>
|
|
<param argument="--valid" type="text"/>
|
|
<param name="param_name" argument="--param-name" type="text"/>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_PARAM_TYPE = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="valid_name"/>
|
|
<param argument="--another-valid-name" type=""/>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_DATA_PARAM = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="valid_name" type="data"/>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_CONDITIONAL = """
|
|
<tool>
|
|
<inputs>
|
|
<conditional>
|
|
<param name="select" type="select"/>
|
|
</conditional>
|
|
<conditional name="cond_wo_param">
|
|
</conditional>
|
|
<conditional name="cond_w_mult_param">
|
|
<param name="select3" type="select"><option value="A">A</option><option value="B">B</option></param>
|
|
<param name="select4" type="select"><option value="A">A</option><option value="B">B</option></param>
|
|
<when value="A"/>
|
|
<when value="B"/>
|
|
</conditional>
|
|
<conditional name="cond_boolean">
|
|
<param name="bool" type="boolean"/>
|
|
<when value="true"/>
|
|
<when value="false"/>
|
|
<when value="False"/>
|
|
</conditional>
|
|
<conditional name="cond_text">
|
|
<param name="text" type="text"/>
|
|
</conditional>
|
|
<conditional name="cond_w_optional_select">
|
|
<param name="optionalselect" type="select" optional="true"><option value="A">A</option><option value="B">B</option></param>
|
|
<when value="A"/>
|
|
<when value="B"/>
|
|
</conditional>
|
|
<conditional name="cond_w_multiple_select">
|
|
<param name="multipleselect" type="select" multiple="true"><option value="A">A</option><option value="B">B</option></param>
|
|
<when value="A"/>
|
|
<when value="B"/>
|
|
</conditional>
|
|
<conditional name="when_wo_value">
|
|
<param name="select3" type="select"><option value="A">A</option><option value="B">B</option></param>
|
|
<when/>
|
|
<when value="A"/>
|
|
<when value="B"/>
|
|
</conditional>
|
|
<conditional name="missing_when">
|
|
<param name="label_select" type="select">
|
|
<option value="none" selected="True">None</option>
|
|
</param>
|
|
</conditional>
|
|
<conditional name="missing_option">
|
|
<param name="missing_option" type="select">
|
|
<option value="none" selected="True">None</option>
|
|
</param>
|
|
<when value="none"/>
|
|
<when value="absent"/>
|
|
</conditional>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_SELECT_INCOMPATIBLE_DISPLAY = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="radio_select" type="select" display="radio" optional="true" multiple="true">
|
|
<option value="1">1</option>
|
|
<option value="2">2</option>
|
|
</param>
|
|
<param name="checkboxes_select" type="select" display="checkboxes" optional="false" multiple="false">
|
|
<option value="1">1</option>
|
|
<option value="2">2</option>
|
|
</param>
|
|
<!-- this must not raise any warning/error since multiple=true implies true as default for optional -->
|
|
<param name="checkboxes_select_correct" type="select" display="checkboxes" multiple="true">
|
|
<option value="1">1</option>
|
|
<option value="2">2</option>
|
|
</param>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_SELECT_DUPLICATED_OPTIONS = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="select" type="select" optional="true" multiple="true">
|
|
<option value="v">x</option>
|
|
<option value="v">x</option>
|
|
</param>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
SELECT_DUPLICATED_OPTIONS_WITH_DIFF_SELECTED = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="select" type="select" optional="true" multiple="true">
|
|
<option value="v">x</option>
|
|
<option value="v" selected="true">x</option>
|
|
</param>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_SELECT_DEPRECATIONS = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="select_do" type="select" dynamic_options="blah()"/>
|
|
<param name="select_ff" type="select">
|
|
<options from_file="file.tsv" transform_lines="narf()"/>
|
|
</param>
|
|
<param name="select_fp" type="select">
|
|
<options from_parameter="select_do" options_filter_attribute="fasel"/>
|
|
</param>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_SELECT_OPTION_DEFINITIONS = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="select_noopt" type="select"/>
|
|
<param name="select_noopts" type="select">
|
|
<options/>
|
|
</param>
|
|
<param name="select_fd_op" type="select">
|
|
<options from_dataset="xyz"/>
|
|
<options from_data_table="xyz"/>
|
|
<option value="x">x</option>
|
|
</param>
|
|
<param name="select_fd_fdt" type="select">
|
|
<options from_dataset="xyz" from_data_table="xyz"/>
|
|
</param>
|
|
<param name="select_noval_notext" type="select">
|
|
<option>option wo value</option>
|
|
<option value="value"/>
|
|
</param>
|
|
<param name="select_meta_file_key_incomp" type="select">
|
|
<options from_data_table="xyz" meta_file_key="dbkey"/>
|
|
</param>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_SELECT_FILTER = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="select_filter_types" type="select">
|
|
<options from_data_table="xyz">
|
|
<filter/>
|
|
<filter type="unknown_filter_type"/>
|
|
</options>
|
|
</param>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_VALIDATOR_INCOMPATIBILITIES = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="param_name" type="text">
|
|
<validator type="in_range">TEXT</validator>
|
|
<validator type="regex" filename="blah"/>
|
|
<validator type="expression"/>
|
|
<validator type="expression">[</validator>
|
|
<validator type="value_in_data_table"/>
|
|
</param>
|
|
<param name="another_param_name" type="data" format="bed">
|
|
<validator type="metadata"/>
|
|
</param>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_VALIDATOR_CORRECT = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="data_param" type="data" format="data">
|
|
<validator type="metadata" check="md1,md2" skip="md3,md4" message="custom validation message" negate="true"/>
|
|
<validator type="unspecified_build" message="custom validation message" negate="true"/>
|
|
<validator type="dataset_ok_validator" message="custom validation message" negate="true"/>
|
|
<validator type="dataset_metadata_in_range" metadata_name="sequences" min="0" max="100" exclude_min="true" exclude_max="true" message="custom validation message" negate="true"/>
|
|
<validator type="dataset_metadata_in_file" filename="file.tsv" metadata_column="3" split="," metadata_name="dbkey" message="custom validation message" negate="true"/>
|
|
<validator type="dataset_metadata_in_data_table" table_name="datatable_name" metadata_column="3" metadata_name="dbkey" message="custom validation message" negate="true"/>
|
|
</param>
|
|
<param name="collection_param" type="collection">
|
|
<validator type="metadata" check="md1,md2" skip="md3,md4" message="custom validation message"/>
|
|
<validator type="unspecified_build" message="custom validation message"/>
|
|
<validator type="dataset_ok_validator" message="custom validation message"/>
|
|
<validator type="dataset_metadata_in_range" metadata_name="sequences" min="0" max="100" exclude_min="true" exclude_max="true" message="custom validation message"/>
|
|
<validator type="dataset_metadata_in_file" filename="file.tsv" metadata_column="3" split="," metadata_name="dbkey" message="custom validation message"/>
|
|
<validator type="dataset_metadata_in_data_table" table_name="datatable_name" metadata_column="3" metadata_name="dbkey" message="custom validation message"/>
|
|
</param>
|
|
<param name="text_param" type="text">
|
|
<validator type="regex">reg.xp</validator>
|
|
<validator type="length" min="0" max="100" message="custom validation message"/>
|
|
<validator type="empty_field" message="custom validation message"/>
|
|
<validator type="value_in_data_table" table_name="datatable_name" metadata_column="3" message="custom validation message"/>
|
|
<validator type="expression" message="custom validation message">somepythonexpression</validator>
|
|
</param>
|
|
<param name="select_param" type="select">
|
|
<options from_data_table="bowtie2_indexes"/>
|
|
<validator type="no_options" negate="true"/>
|
|
<validator type="regex" negate="true">reg.xp</validator>
|
|
<validator type="length" min="0" max="100" message="custom validation message" negate="true"/>
|
|
<validator type="empty_field" message="custom validation message" negate="true"/>
|
|
<validator type="value_in_data_table" table_name="datatable_name" metadata_column="3" message="custom validation message" negate="true"/>
|
|
<validator type="expression" message="custom validation message" negate="true">somepythonexpression</validator>
|
|
</param>
|
|
<param name="int_param" type="integer">
|
|
<validator type="in_range" min="0" max="100" exclude_min="true" exclude_max="true" negate="true"/>
|
|
<validator type="expression" message="custom validation message">somepythonexpression</validator>
|
|
</param>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_TYPE_CHILD_COMBINATIONS = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="text_param" type="text">
|
|
<options/>
|
|
</param>
|
|
<param name="select_param" type="select">
|
|
<options from_data_table="data_table">
|
|
<option name="x" value="y"/>
|
|
</options>
|
|
</param>
|
|
<param name="data_param" type="data" format="tabular">
|
|
<column/>
|
|
</param>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
INPUTS_DUPLICATE_NAMES = """
|
|
<tool>
|
|
<inputs>
|
|
<param name="dup" type="text"/>
|
|
<param name="dup" type="text"/>
|
|
<param name="dup_in_section" type="text"/>
|
|
<section name="sec">
|
|
<param name="dup_in_section" type="text"/>
|
|
</section>
|
|
<conditional name="cond">
|
|
<param name="dup_in_cond" type="select">
|
|
<option value="a">a</option>
|
|
<option value="b">b</option>
|
|
</param>
|
|
<when value="a">
|
|
<param name="dup_in_cond" type="text"/>
|
|
</when>
|
|
<when value="b">
|
|
<param name="dup_in_cond" type="text"/>
|
|
</when>
|
|
</conditional>
|
|
<param name="dup_in_output" type="text"/>
|
|
</inputs>
|
|
<outputs>
|
|
<data name="dup_in_output"/>
|
|
</outputs>
|
|
</tool>
|
|
"""
|
|
|
|
# test tool xml for outputs linter
|
|
OUTPUTS_MISSING = """
|
|
<tool/>
|
|
"""
|
|
OUTPUTS_MULTIPLE = """
|
|
<tool>
|
|
<outputs/>
|
|
<outputs/>
|
|
</tool>
|
|
"""
|
|
OUTPUTS_UNKNOWN_TAG = """
|
|
<tool>
|
|
<outputs>
|
|
<output/>
|
|
</outputs>
|
|
</tool>
|
|
"""
|
|
OUTPUTS_UNNAMED_INVALID_NAME = """
|
|
<tool>
|
|
<outputs>
|
|
<data label="data out"/>
|
|
<collection name="2output" label="coll out"/>
|
|
</outputs>
|
|
</tool>
|
|
"""
|
|
OUTPUTS_FORMAT_INPUT = """
|
|
<tool>
|
|
<outputs>
|
|
<data name="valid_name" format="input"/>
|
|
</outputs>
|
|
</tool>
|
|
"""
|
|
|
|
# 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 = """
|
|
<tool>
|
|
<outputs>
|
|
<collection name="output_collection" type="paired">
|
|
<data name="forward" format_source="input_readpair" />
|
|
<data name="reverse" format_source="input_readpair" format="fastq"/>
|
|
</collection>
|
|
</outputs>
|
|
</tool>
|
|
"""
|
|
|
|
# check that setting format with actions is supported
|
|
OUTPUTS_FORMAT_ACTION = """
|
|
<tool>
|
|
<outputs>
|
|
<data name="output">
|
|
<actions>
|
|
<conditional name="library.type">
|
|
<when value="paired">
|
|
<action type="format">
|
|
<option type="from_param" name="library.input_2" param_attribute="ext" />
|
|
</action>
|
|
</when>
|
|
</conditional>
|
|
</actions>
|
|
</data>
|
|
</outputs>
|
|
</tool>
|
|
"""
|
|
|
|
# check that linter does not complain about missing format if from_tool_provided_metadata is used
|
|
OUTPUTS_DISCOVER_TOOL_PROVIDED_METADATA = """
|
|
<tool>
|
|
<outputs>
|
|
<data name="output">
|
|
<discover_datasets from_tool_provided_metadata="true"/>
|
|
</data>
|
|
</outputs>
|
|
</tool>
|
|
"""
|
|
OUTPUTS_DUPLICATED_NAME_LABEL = """
|
|
<tool>
|
|
<outputs>
|
|
<data name="valid_name" format="fasta"/>
|
|
<data name="valid_name" format="fasta"/>
|
|
</outputs>
|
|
</tool>
|
|
"""
|
|
|
|
# tool xml for repeats linter
|
|
REPEATS = """
|
|
<tool>
|
|
<inputs>
|
|
<repeat>
|
|
<param name="another_param_name" type="data" format="bed"/>
|
|
</repeat>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
# tool xml for stdio linter
|
|
STDIO_DEFAULT_FOR_DEFAULT_PROFILE = """
|
|
<tool>
|
|
</tool>
|
|
"""
|
|
|
|
STDIO_DEFAULT_FOR_NONLEGACY_PROFILE = """
|
|
<tool profile="21.09">
|
|
</tool>
|
|
"""
|
|
|
|
STDIO_MULTIPLE_STDIO = """
|
|
<tool>
|
|
<stdio/>
|
|
<stdio/>
|
|
</tool>
|
|
"""
|
|
|
|
STDIO_INVALID_CHILD_OR_ATTRIB = """
|
|
<tool>
|
|
<stdio>
|
|
<reqex/>
|
|
<regex descriptio="blah" level="fatal" match="error" source="stdio"/>
|
|
<exit_code descriptio="blah" level="fatal" range="1:"/>
|
|
</stdio>
|
|
</tool>
|
|
"""
|
|
|
|
STDIO_INVALID_MATCH = """
|
|
<tool>
|
|
<stdio>
|
|
<regex match="["/>
|
|
</stdio>
|
|
</tool>
|
|
"""
|
|
|
|
# check that linter does complain about tests wo assumptions
|
|
TESTS_ABSENT = """
|
|
<tool/>
|
|
"""
|
|
TESTS_ABSENT_DATA_SOURCE = """
|
|
<tool tool_type="data_source"/>
|
|
"""
|
|
TESTS_WO_EXPECTATIONS = """
|
|
<tool>
|
|
<tests>
|
|
<test>
|
|
</test>
|
|
</tests>
|
|
</tool>
|
|
"""
|
|
|
|
TESTS_PARAM_OUTPUT_NAMES = """
|
|
<tool>
|
|
<inputs>
|
|
<param argument="--existent-test-name"/>
|
|
<conditional>
|
|
<when>
|
|
<param name="another_existent_test_name"/>
|
|
</when>
|
|
</conditional>
|
|
</inputs>
|
|
<outputs>
|
|
<data name="existent_output"/>
|
|
<collection name="existent_collection"/>
|
|
</outputs>
|
|
<tests>
|
|
<test expect_num_outputs="1">
|
|
<param/>
|
|
<param name="existent_test_name"/>
|
|
<param name="cond_name|another_existent_test_name"/>
|
|
<param name="non_existent_test_name"/>
|
|
<output/>
|
|
<output name="existent_output"/>
|
|
<output name="nonexistent_output"/>
|
|
<output_collection/>
|
|
<output_collection name="existent_collection"/>
|
|
<output_collection name="nonexistent_collection"/>
|
|
</test>
|
|
</tests>
|
|
</tool>
|
|
"""
|
|
|
|
TESTS_EXPECT_FAILURE_OUTPUT = """
|
|
<tool>
|
|
<outputs>
|
|
<data name="test"/>
|
|
</outputs>
|
|
<tests>
|
|
<test expect_failure="true">
|
|
<output name="test"/>
|
|
</test>
|
|
<test expect_num_outputs="1" expect_failure="true"/>
|
|
</tests>
|
|
</tool>
|
|
"""
|
|
|
|
ASSERTS = """
|
|
<tool>
|
|
<outputs>
|
|
<data name="out_archive"/>
|
|
<data name="out_tabular"/>
|
|
</outputs>
|
|
<tests>
|
|
<test>
|
|
<assert_stdout>
|
|
<has_text text="blah" n="1"/>
|
|
<has_line line="blah"/>
|
|
</assert_stdout>
|
|
<assert_stderr>
|
|
<invalid/>
|
|
</assert_stderr>
|
|
<assert_command>
|
|
<has_text invalid_attrib="blah"/>
|
|
</assert_command>
|
|
<output name="out_archive">
|
|
<assert_contents>
|
|
<has_size value="500k" delta="1O"/>
|
|
<has_archive_member path=".*/my-file.txt">
|
|
<not_has_text invalid_attrib_also_checked_in_nested_asserts="Blah" text="EDK72998.1" />
|
|
</has_archive_member>
|
|
</assert_contents>
|
|
</output>
|
|
<output name="out_tabular">
|
|
<assert_contents>
|
|
<has_size/>
|
|
<has_n_columns/>
|
|
<has_n_lines/>
|
|
</assert_contents>
|
|
</output>
|
|
</test>
|
|
</tests>
|
|
</tool>
|
|
"""
|
|
TESTS_VALID = """
|
|
<tool>
|
|
<outputs>
|
|
<data name="test"/>
|
|
</outputs>
|
|
<tests>
|
|
<test>
|
|
<output name="test"/>
|
|
</test>
|
|
</tests>
|
|
</tool>
|
|
"""
|
|
TESTS_OUTPUT_TYPE_MISMATCH = """
|
|
<tool>
|
|
<outputs>
|
|
<data name="data_name"/>
|
|
<collection name="collection_name" type="list:list"/>
|
|
</outputs>
|
|
<tests>
|
|
<test>
|
|
<output_collection name="data_name"/>
|
|
<output name="collection_name"/>
|
|
</test>
|
|
</tests>
|
|
</tool>
|
|
"""
|
|
TESTS_DISCOVER_OUTPUTS = """
|
|
<tool>
|
|
<outputs>
|
|
<data name="data_name">
|
|
<discover_datasets/>
|
|
</data>
|
|
<collection name="collection_name" type="list:list">
|
|
<discover_datasets/>
|
|
</collection>
|
|
</outputs>
|
|
<tests>
|
|
<!-- this should be fine -->
|
|
<test>
|
|
<output name="data_name">
|
|
<discovered_dataset/>
|
|
</output>
|
|
<output_collection name="collection_name">
|
|
<element count="2"/>
|
|
</output_collection>
|
|
</test>
|
|
<!-- this should be fine as well -->
|
|
<test>
|
|
<output name="data_name" count="2"/>
|
|
<output_collection name="collection_name">
|
|
<element>
|
|
<element/>
|
|
</element>
|
|
</output_collection>
|
|
</test>
|
|
<!-- no count or discovered_dataset/element -->
|
|
<test>
|
|
<output name="data_name"/>
|
|
<output_collection name="collection_name"/>
|
|
</test>
|
|
<!-- no nested element and count at element -->
|
|
<test>
|
|
<output name="data_name" count="1"/>
|
|
<output_collection name="collection_name" count="1">
|
|
<element/>
|
|
</output_collection>
|
|
</test>
|
|
</tests>
|
|
</tool>
|
|
"""
|
|
|
|
TESTS_EXPECT_NUM_OUTPUTS_FILTER = """
|
|
<tool>
|
|
<outputs>
|
|
<data>
|
|
<filter/>
|
|
</data>
|
|
</outputs>
|
|
<tests>
|
|
<test expect_failure="false">
|
|
</test>
|
|
</tests>
|
|
</tool>
|
|
"""
|
|
|
|
TESTS_COMPARE_ATTRIB_INCOMPATIBILITY = """
|
|
<tool>
|
|
<outputs>
|
|
<data name="data_name"/>
|
|
<collection name="collection_name" type="list:list"/>
|
|
</outputs>
|
|
<tests>
|
|
<test>
|
|
<output name="data_name" compare="re_match" decompress="true"/>
|
|
<output_collection name="collection_name">
|
|
<element compare="contains" sort="true" />
|
|
</output_collection>
|
|
</test>
|
|
<test>
|
|
<output name="data_name" compare="diff" lines_diff="2"/>
|
|
<output_collection name="collection_name">
|
|
<element compare="contains" lines_diff="2" />
|
|
</output_collection>
|
|
</test>
|
|
</tests>
|
|
</tool>"""
|
|
|
|
# tool xml for xml_order linter
|
|
XML_ORDER = """
|
|
<tool>
|
|
<wrong_tag/>
|
|
<command/>
|
|
<stdio/>
|
|
</tool>
|
|
"""
|
|
|
|
TOOL_WITH_COMMENTS = """
|
|
<tool>
|
|
<stdio>
|
|
<!-- This is a comment -->
|
|
</stdio>
|
|
<outputs>
|
|
<!-- This is a comment -->
|
|
</outputs>
|
|
</tool>
|
|
"""
|
|
|
|
|
|
@pytest.fixture()
|
|
def lint_ctx():
|
|
return LintContext("all", lint_message_class=XMLLintMessageLine)
|
|
|
|
|
|
@pytest.fixture()
|
|
def lint_ctx_xpath():
|
|
return LintContext("all", lint_message_class=XMLLintMessageXPath)
|
|
|
|
|
|
def get_xml_tool_source(xml_string):
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix="tool.xml") as tmp:
|
|
tmp.write(xml_string)
|
|
tmp.flush()
|
|
tool_path = tmp.name
|
|
return load_with_references(tool_path)[0]
|
|
|
|
|
|
def get_tool_xml_exact(xml_string):
|
|
"""Returns the tool XML as it is, without stripping comments or anything else."""
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix="tool.xml") as tmp:
|
|
tmp.write(xml_string)
|
|
tmp.flush()
|
|
tool_path = tmp.name
|
|
return parse_xml(tool_path, strip_whitespace=False, remove_comments=False)
|
|
|
|
|
|
def failed_assert_print(lint_ctx):
|
|
return (
|
|
f"Valid: {lint_ctx.valid_messages}\n"
|
|
f"Info: {lint_ctx.info_messages}\n"
|
|
f"Warnings: {lint_ctx.warn_messages}\n"
|
|
f"Errors: {lint_ctx.error_messages}"
|
|
)
|
|
|
|
|
|
def run_lint(lint_ctx, lint_func, lint_target):
|
|
lint_ctx.lint(name="test_lint", lint_func=lint_func, lint_target=lint_target)
|
|
# check if the lint messages have the line
|
|
for message in lint_ctx.message_list:
|
|
if lint_func != general.lint_general:
|
|
assert message.line is not None, f"No context found for message: {message.message}"
|
|
|
|
|
|
def test_citations_multiple(lint_ctx):
|
|
tool_source = get_xml_tool_source(CITATIONS_MULTIPLE)
|
|
run_lint(lint_ctx, citations.lint_citations, tool_source)
|
|
assert "More than one citation section found, behavior undefined." in lint_ctx.error_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 1
|
|
|
|
|
|
def test_citations_absent(lint_ctx):
|
|
tool_source = get_xml_tool_source(CITATIONS_ABSENT)
|
|
run_lint(lint_ctx, citations.lint_citations, tool_source)
|
|
assert lint_ctx.warn_messages == ["No citations found, consider adding citations to your tool."]
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_citations_errors(lint_ctx):
|
|
tool_source = get_xml_tool_source(CITATIONS_ERRORS)
|
|
run_lint(lint_ctx, citations.lint_citations, tool_source)
|
|
assert "Unknown tag discovered in citations block [nonsense], will be ignored." in lint_ctx.warn_messages
|
|
assert "Unknown citation type discovered [hoerensagen], will be ignored." in lint_ctx.warn_messages
|
|
assert "Empty doi citation." in lint_ctx.error_messages
|
|
assert "Found no valid citations." in lint_ctx.warn_messages
|
|
assert len(lint_ctx.warn_messages) == 3
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
|
|
|
|
def test_citations_valid(lint_ctx):
|
|
tool_source = get_xml_tool_source(CITATIONS_VALID)
|
|
run_lint(lint_ctx, citations.lint_citations, tool_source)
|
|
assert "Found 1 likely valid citations." in lint_ctx.valid_messages
|
|
assert len(lint_ctx.valid_messages) == 1
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_command_multiple(lint_ctx):
|
|
tool_source = get_xml_tool_source(COMMAND_MULTIPLE)
|
|
run_lint(lint_ctx, command.lint_command, tool_source)
|
|
assert "More than one command tag found, behavior undefined." in lint_ctx.error_messages
|
|
assert len(lint_ctx.error_messages) == 1
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
|
|
|
|
def test_command_missing(lint_ctx):
|
|
tool_source = get_xml_tool_source(COMMAND_MISSING)
|
|
run_lint(lint_ctx, command.lint_command, tool_source)
|
|
assert "No command tag found, must specify a command template to execute." in lint_ctx.error_messages
|
|
|
|
|
|
def test_command_todo(lint_ctx):
|
|
tool_source = get_xml_tool_source(COMMAND_TODO)
|
|
run_lint(lint_ctx, command.lint_command, tool_source)
|
|
assert "Tool contains a command." in lint_ctx.info_messages
|
|
assert "Command template contains TODO text." in lint_ctx.warn_messages
|
|
|
|
|
|
def test_command_detect_errors_interpreter(lint_ctx):
|
|
tool_source = get_xml_tool_source(COMMAND_DETECT_ERRORS_INTERPRETER)
|
|
run_lint(lint_ctx, command.lint_command, tool_source)
|
|
assert "Command uses deprecated 'interpreter' attribute." in lint_ctx.warn_messages
|
|
assert "Tool contains a command with interpreter of type [python]." in lint_ctx.info_messages
|
|
assert "Unknown detect_errors attribute [nonsense]" in lint_ctx.warn_messages
|
|
assert "Command is empty." in lint_ctx.error_messages
|
|
|
|
|
|
def test_general_missing_tool_id_name_version(lint_ctx):
|
|
tool_source = get_xml_tool_source(GENERAL_MISSING_TOOL_ID_NAME_VERSION)
|
|
run_lint(lint_ctx, general.lint_general, XmlToolSource(tool_source))
|
|
assert "Tool version is missing or empty." in lint_ctx.error_messages
|
|
assert "Tool name is missing or empty." in lint_ctx.error_messages
|
|
assert "Tool does not define an id attribute." in lint_ctx.error_messages
|
|
assert "Tool specifies an invalid profile version [2109]." in lint_ctx.error_messages
|
|
|
|
|
|
def test_general_whitespace_in_versions_and_names(lint_ctx):
|
|
tool_source = get_xml_tool_source(GENERAL_WHITESPACE_IN_VERSIONS_AND_NAMES)
|
|
run_lint(lint_ctx, general.lint_general, XmlToolSource(tool_source))
|
|
assert "Tool version is pre/suffixed by whitespace, this may cause errors: [ 1.0.1 ]." in lint_ctx.warn_messages
|
|
assert "Tool name is pre/suffixed by whitespace, this may cause errors: [ BWA Mapper ]." in lint_ctx.warn_messages
|
|
assert "Requirement version contains whitespace, this may cause errors: [ 1.2.5 ]." in lint_ctx.warn_messages
|
|
assert "Tool ID contains whitespace - this is discouraged: [bwa tool]." in lint_ctx.warn_messages
|
|
assert "Tool targets 16.01 Galaxy profile." in lint_ctx.valid_messages
|
|
|
|
|
|
def test_general_requirement_without_version(lint_ctx):
|
|
tool_source = get_xml_tool_source(GENERAL_REQUIREMENT_WO_VERSION)
|
|
run_lint(lint_ctx, general.lint_general, XmlToolSource(tool_source))
|
|
assert "Tool version [1.0.1blah] is not compliant with PEP 440." in lint_ctx.warn_messages
|
|
assert "Requirement bwa defines no version" in lint_ctx.warn_messages
|
|
assert "Requirement without name found" in lint_ctx.error_messages
|
|
assert "Tool specifies profile version [20.09]." in lint_ctx.valid_messages
|
|
assert "Tool defines an id [bwa_tool]." in lint_ctx.valid_messages
|
|
assert "Tool defines a name [BWA Mapper]." in lint_ctx.valid_messages
|
|
assert not lint_ctx.info_messages
|
|
assert len(lint_ctx.valid_messages) == 3
|
|
assert len(lint_ctx.warn_messages) == 2
|
|
assert len(lint_ctx.error_messages) == 1
|
|
|
|
|
|
def test_general_valid(lint_ctx):
|
|
tool_source = get_xml_tool_source(GENERAL_VALID)
|
|
run_lint(lint_ctx, general.lint_general, XmlToolSource(tool_source))
|
|
assert "Tool defines a version [1.0+galaxy1]." in lint_ctx.valid_messages
|
|
assert "Tool specifies profile version [21.09]." in lint_ctx.valid_messages
|
|
assert "Tool defines an id [valid_id]." in lint_ctx.valid_messages
|
|
assert "Tool defines a name [valid name]." in lint_ctx.valid_messages
|
|
assert not lint_ctx.info_messages
|
|
assert len(lint_ctx.valid_messages) == 4
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_help_multiple(lint_ctx):
|
|
tool_source = get_xml_tool_source(HELP_MULTIPLE)
|
|
run_lint(lint_ctx, help.lint_help, tool_source)
|
|
assert "More than one help section found, behavior undefined." in lint_ctx.error_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 1
|
|
|
|
|
|
def test_help_absent(lint_ctx):
|
|
tool_source = get_xml_tool_source(HELP_ABSENT)
|
|
run_lint(lint_ctx, help.lint_help, tool_source)
|
|
assert "No help section found, consider adding a help section to your tool." in lint_ctx.warn_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_help_empty(lint_ctx):
|
|
tool_source = get_xml_tool_source(HELP_EMPTY)
|
|
run_lint(lint_ctx, help.lint_help, tool_source)
|
|
assert "Help section appears to be empty." in lint_ctx.warn_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_help_todo(lint_ctx):
|
|
tool_source = get_xml_tool_source(HELP_TODO)
|
|
run_lint(lint_ctx, help.lint_help, tool_source)
|
|
assert "Tool contains help section." in lint_ctx.valid_messages
|
|
assert "Help contains valid reStructuredText." in lint_ctx.valid_messages
|
|
assert "Help contains TODO text." in lint_ctx.warn_messages
|
|
assert not lint_ctx.info_messages
|
|
assert len(lint_ctx.valid_messages) == 2
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_help_invalid_rst(lint_ctx):
|
|
tool_source = get_xml_tool_source(HELP_INVALID_RST)
|
|
run_lint(lint_ctx, help.lint_help, tool_source)
|
|
assert "Tool contains help section." in lint_ctx.valid_messages
|
|
assert (
|
|
"Invalid reStructuredText found in help - [<string>:2: (WARNING/2) Inline strong start-string without end-string.\n]."
|
|
in lint_ctx.warn_messages
|
|
)
|
|
assert not lint_ctx.info_messages
|
|
assert len(lint_ctx.valid_messages) == 1
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_inputs_no_inputs(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_NO_INPUTS)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found no input parameters." in lint_ctx.warn_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_inputs_no_inputs_datasource(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_NO_INPUTS_DATASOURCE)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "No input parameters, OK for data sources" in lint_ctx.info_messages
|
|
assert "display tag usually present in data sources" in lint_ctx.info_messages
|
|
assert "uihints tag usually present in data sources" in lint_ctx.info_messages
|
|
assert len(lint_ctx.info_messages) == 3
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_inputs_valid(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_VALID)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 2 input parameters." in lint_ctx.info_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_inputs_param_name(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_PARAM_NAME)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 5 input parameters." in lint_ctx.info_messages
|
|
assert "Param input [2] is not a valid Cheetah placeholder." in lint_ctx.warn_messages
|
|
assert "Found param input with no name specified." in lint_ctx.error_messages
|
|
assert "Param input with empty name." in lint_ctx.error_messages
|
|
assert (
|
|
"Param input [param_name] 'name' attribute is redundant if argument implies the same name."
|
|
in lint_ctx.warn_messages
|
|
)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 2
|
|
assert len(lint_ctx.error_messages) == 2
|
|
|
|
|
|
def test_inputs_param_type(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_PARAM_TYPE)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 2 input parameters." in lint_ctx.info_messages
|
|
assert "Param input [valid_name] input with no type specified." in lint_ctx.error_messages
|
|
assert "Param input [another_valid_name] with empty type specified." in lint_ctx.error_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 2
|
|
|
|
|
|
def test_inputs_data_param(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_DATA_PARAM)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 1 input parameters." in lint_ctx.info_messages
|
|
assert (
|
|
"Param input [valid_name] with no format specified - 'data' format will be assumed." in lint_ctx.warn_messages
|
|
)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_inputs_conditional(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_CONDITIONAL)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 10 input parameters." in lint_ctx.info_messages
|
|
assert "Conditional without a name" in lint_ctx.error_messages
|
|
assert (
|
|
"Select parameter of a conditional [select] options have to be defined by 'option' children elements."
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert "Conditional [cond_wo_param] needs exactly one child <param> found 0" in lint_ctx.error_messages
|
|
assert "Conditional [cond_w_mult_param] needs exactly one child <param> found 2" in lint_ctx.error_messages
|
|
assert 'Conditional [cond_text] first param should have type="select"' in lint_ctx.error_messages
|
|
assert (
|
|
'Conditional [cond_boolean] first param of type="boolean" is discouraged, use a select'
|
|
in lint_ctx.warn_messages
|
|
)
|
|
assert "Conditional [cond_boolean] no truevalue/falsevalue found for when block 'False'" in lint_ctx.warn_messages
|
|
assert 'Conditional [cond_w_optional_select] test parameter cannot be optional="true"' in lint_ctx.warn_messages
|
|
assert 'Conditional [cond_w_multiple_select] test parameter cannot be multiple="true"' in lint_ctx.warn_messages
|
|
assert "Conditional [when_wo_value] when without value" in lint_ctx.error_messages
|
|
assert "Conditional [missing_when] no <when /> block found for select option 'none'" in lint_ctx.warn_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 6
|
|
assert len(lint_ctx.error_messages) == 6
|
|
|
|
|
|
def test_inputs_select_incompatible_display(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_SELECT_INCOMPATIBLE_DISPLAY)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 3 input parameters." in lint_ctx.info_messages
|
|
assert 'Select [radio_select] display="radio" is incompatible with optional="true"' in lint_ctx.error_messages
|
|
assert 'Select [radio_select] display="radio" is incompatible with multiple="true"' in lint_ctx.error_messages
|
|
assert (
|
|
'Select [checkboxes_select] `display="checkboxes"` is incompatible with `optional="false"`, remove the `display` attribute'
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
'Select [checkboxes_select] `display="checkboxes"` is incompatible with `multiple="false"`, remove the `display` attribute'
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 4
|
|
|
|
|
|
def test_inputs_duplicated_options(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_SELECT_DUPLICATED_OPTIONS)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 1 input parameters." in lint_ctx.info_messages
|
|
assert "Select parameter [select] has multiple options with the same text content" in lint_ctx.error_messages
|
|
assert "Select parameter [select] has multiple options with the same value" in lint_ctx.error_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 2
|
|
|
|
|
|
def test_inputs_duplicated_options_with_different_select(lint_ctx):
|
|
tool_source = get_xml_tool_source(SELECT_DUPLICATED_OPTIONS_WITH_DIFF_SELECTED)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_inputs_select_deprections(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_SELECT_DEPRECATIONS)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 3 input parameters." in lint_ctx.info_messages
|
|
assert "Select parameter [select_do] uses deprecated 'dynamic_options' attribute." in lint_ctx.warn_messages
|
|
assert "Select parameter [select_ff] options uses deprecated 'from_file' attribute." in lint_ctx.warn_messages
|
|
assert "Select parameter [select_fp] options uses deprecated 'from_parameter' attribute." in lint_ctx.warn_messages
|
|
assert "Select parameter [select_ff] options uses deprecated 'transform_lines' attribute." in lint_ctx.warn_messages
|
|
assert (
|
|
"Select parameter [select_fp] options uses deprecated 'options_filter_attribute' attribute."
|
|
in lint_ctx.warn_messages
|
|
)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 5
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_inputs_select_option_definitions(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_SELECT_OPTION_DEFINITIONS)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 6 input parameters." in lint_ctx.info_messages
|
|
assert (
|
|
"Select parameter [select_noopt] options have to be defined by either 'option' children elements, a 'options' element or the 'dynamic_options' attribute."
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Select parameter [select_noopts] options tag defines no options. Use 'from_dataset', 'from_data_table', or a filter that adds values."
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Select parameter [select_fd_op] options have to be defined by either 'option' children elements, a 'options' element or the 'dynamic_options' attribute."
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert "Select parameter [select_fd_op] contains multiple options elements." in lint_ctx.error_messages
|
|
assert (
|
|
"Select parameter [select_fd_fdt] options uses 'from_dataset' and 'from_data_table' attribute."
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert "Select parameter [select_noval_notext] has option without value" in lint_ctx.error_messages
|
|
assert "Select parameter [select_noval_notext] has option without text" in lint_ctx.warn_messages
|
|
assert (
|
|
"Select parameter [select_meta_file_key_incomp] 'meta_file_key' is only compatible with 'from_dataset'."
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert len(lint_ctx.error_messages) == 7
|
|
|
|
|
|
def test_inputs_select_filter(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_SELECT_FILTER)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 1 input parameters." in lint_ctx.info_messages
|
|
assert "Select parameter [select_filter_types] contains filter without type." in lint_ctx.error_messages
|
|
assert (
|
|
"Select parameter [select_filter_types] contains filter with unknown type 'unknown_filter_type'."
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 2
|
|
|
|
|
|
def test_inputs_validator_incompatibilities(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_VALIDATOR_INCOMPATIBILITIES)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 2 input parameters." in lint_ctx.info_messages
|
|
assert (
|
|
"Parameter [param_name]: 'in_range' validators are not expected to contain text (found 'TEXT')"
|
|
in lint_ctx.warn_messages
|
|
)
|
|
assert "Parameter [param_name]: validator with an incompatible type 'in_range'" in lint_ctx.error_messages
|
|
assert (
|
|
"Parameter [param_name]: 'in_range' validators need to define the 'min' or 'max' attribute(s)"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Parameter [param_name]: attribute 'filename' is incompatible with validator of type 'regex'"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert "Parameter [param_name]: expression validators are expected to contain text" in lint_ctx.error_messages
|
|
assert (
|
|
"Parameter [param_name]: '[' is no valid regular expression: unterminated character set at position 0"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Parameter [another_param_name]: 'metadata' validators need to define the 'check' or 'skip' attribute(s)"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Parameter [param_name]: 'value_in_data_table' validators need to define the 'table_name' attribute"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert len(lint_ctx.error_messages) == 7
|
|
|
|
|
|
def test_inputs_validator_correct(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_VALIDATOR_CORRECT)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert "Found 5 input parameters." in lint_ctx.info_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_inputs_type_child_combinations(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_TYPE_CHILD_COMBINATIONS)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert (
|
|
"Parameter [text_param] './options' tags are only allowed for parameters of type ['select', 'drill_down']"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Parameter [select_param] './options/option' tags are only allowed for parameters of type ['drill_down']"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Parameter [data_param] './column' tags are only allowed for parameters of type ['data_column']"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert len(lint_ctx.error_messages) == 3
|
|
|
|
|
|
def test_inputs_duplicate_names(lint_ctx):
|
|
tool_source = get_xml_tool_source(INPUTS_DUPLICATE_NAMES)
|
|
run_lint(lint_ctx, inputs.lint_inputs, tool_source)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert "Tool defines multiple parameters with the same name: 'dup'" in lint_ctx.error_messages
|
|
assert (
|
|
"Tool defines an output with a name equal to the name of an input: 'dup_in_output'" in lint_ctx.error_messages
|
|
)
|
|
assert len(lint_ctx.error_messages) == 2
|
|
|
|
|
|
def test_inputs_repeats(lint_ctx):
|
|
tool_source = get_xml_tool_source(REPEATS)
|
|
run_lint(lint_ctx, inputs.lint_repeats, tool_source)
|
|
assert "Repeat does not specify name attribute." in lint_ctx.error_messages
|
|
assert "Repeat does not specify title attribute." in lint_ctx.error_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 2
|
|
|
|
|
|
def test_outputs_missing(lint_ctx):
|
|
tool_source = get_xml_tool_source(OUTPUTS_MISSING)
|
|
run_lint(lint_ctx, outputs.lint_output, tool_source)
|
|
assert "Tool contains no outputs section, most tools should produce outputs." in lint_ctx.warn_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_outputs_multiple(lint_ctx):
|
|
tool_source = get_xml_tool_source(OUTPUTS_MULTIPLE)
|
|
run_lint(lint_ctx, outputs.lint_output, tool_source)
|
|
assert "0 outputs found." in lint_ctx.info_messages
|
|
assert "Tool contains multiple output sections, behavior undefined." in lint_ctx.warn_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_outputs_unknown_tag(lint_ctx):
|
|
tool_source = get_xml_tool_source(OUTPUTS_UNKNOWN_TAG)
|
|
run_lint(lint_ctx, outputs.lint_output, tool_source)
|
|
assert "0 outputs found." in lint_ctx.info_messages
|
|
assert "Unknown element found in outputs [output]" in lint_ctx.warn_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_outputs_unnamed_invalid_name(lint_ctx):
|
|
tool_source = get_xml_tool_source(OUTPUTS_UNNAMED_INVALID_NAME)
|
|
run_lint(lint_ctx, outputs.lint_output, tool_source)
|
|
assert "2 outputs found." in lint_ctx.info_messages
|
|
assert "Tool output doesn't define a name - this is likely a problem." in lint_ctx.warn_messages
|
|
assert "Tool data output with missing name doesn't define an output format." in lint_ctx.warn_messages
|
|
assert "Tool output name [2output] is not a valid Cheetah placeholder." in lint_ctx.warn_messages
|
|
assert "Collection output with undefined 'type' found." in lint_ctx.warn_messages
|
|
assert "Tool collection output 2output doesn't define an output format." in lint_ctx.warn_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 5
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_outputs_format_input(lint_ctx):
|
|
tool_source = get_xml_tool_source(OUTPUTS_FORMAT_INPUT)
|
|
run_lint(lint_ctx, outputs.lint_output, tool_source)
|
|
assert "1 outputs found." in lint_ctx.info_messages
|
|
assert (
|
|
"Using format='input' on data, format_source attribute is less ambiguous and should be used instead."
|
|
in lint_ctx.warn_messages
|
|
)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_outputs_collection_format_source(lint_ctx):
|
|
tool_source = get_xml_tool_source(OUTPUTS_COLLECTION_FORMAT_SOURCE)
|
|
run_lint(lint_ctx, outputs.lint_output, tool_source)
|
|
assert "Tool data output 'reverse' should use either format_source or format/ext" in lint_ctx.warn_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_outputs_format_action(lint_ctx):
|
|
tool_source = get_xml_tool_source(OUTPUTS_FORMAT_ACTION)
|
|
run_lint(lint_ctx, outputs.lint_output, tool_source)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_outputs_discover_tool_provided_metadata(lint_ctx):
|
|
tool_source = get_xml_tool_source(OUTPUTS_DISCOVER_TOOL_PROVIDED_METADATA)
|
|
run_lint(lint_ctx, outputs.lint_output, tool_source)
|
|
assert "1 outputs found." in lint_ctx.info_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_outputs_duplicated_name_label(lint_ctx):
|
|
tool_source = get_xml_tool_source(OUTPUTS_DUPLICATED_NAME_LABEL)
|
|
run_lint(lint_ctx, outputs.lint_output, tool_source)
|
|
assert "2 outputs found." in lint_ctx.info_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert "Tool output [valid_name] has duplicated name" in lint_ctx.error_messages
|
|
assert "Tool output [valid_name] uses duplicated label '${tool.name} on ${on_string}'" in lint_ctx.error_messages
|
|
assert len(lint_ctx.error_messages) == 2
|
|
|
|
|
|
def test_stdio_default_for_default_profile(lint_ctx):
|
|
tool_source = get_xml_tool_source(STDIO_DEFAULT_FOR_DEFAULT_PROFILE)
|
|
run_lint(lint_ctx, stdio.lint_stdio, XmlToolSource(tool_source))
|
|
assert (
|
|
"No stdio definition found, tool indicates error conditions with output written to stderr."
|
|
in lint_ctx.info_messages
|
|
)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_stdio_default_for_nonlegacy_profile(lint_ctx):
|
|
tool_source = get_xml_tool_source(STDIO_DEFAULT_FOR_NONLEGACY_PROFILE)
|
|
run_lint(lint_ctx, stdio.lint_stdio, XmlToolSource(tool_source))
|
|
assert (
|
|
"No stdio definition found, tool indicates error conditions with non-zero exit codes." in lint_ctx.info_messages
|
|
)
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_stdio_multiple_stdio(lint_ctx):
|
|
tool_source = get_xml_tool_source(STDIO_MULTIPLE_STDIO)
|
|
run_lint(lint_ctx, stdio.lint_stdio, XmlToolSource(tool_source))
|
|
assert "More than one stdio tag found, behavior undefined." in lint_ctx.error_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 1
|
|
|
|
|
|
def test_stdio_invalid_child_or_attrib(lint_ctx):
|
|
tool_source = get_xml_tool_source(STDIO_INVALID_CHILD_OR_ATTRIB)
|
|
run_lint(lint_ctx, stdio.lint_stdio, XmlToolSource(tool_source))
|
|
assert (
|
|
"Unknown stdio child tag discovered [reqex]. Valid options are exit_code and regex." in lint_ctx.warn_messages
|
|
)
|
|
assert "Unknown attribute [descriptio] encountered on exit_code tag." in lint_ctx.warn_messages
|
|
assert "Unknown attribute [descriptio] encountered on regex tag." in lint_ctx.warn_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 3
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_stdio_invalid_match(lint_ctx):
|
|
tool_source = get_xml_tool_source(STDIO_INVALID_MATCH)
|
|
run_lint(lint_ctx, stdio.lint_stdio, XmlToolSource(tool_source))
|
|
assert (
|
|
"Match '[' is no valid regular expression: unterminated character set at position 0" in lint_ctx.error_messages
|
|
)
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 1
|
|
|
|
|
|
def test_tests_absent(lint_ctx):
|
|
tool_source = get_xml_tool_source(TESTS_ABSENT)
|
|
run_lint(lint_ctx, tests.lint_tests, tool_source)
|
|
assert "No tests found, most tools should define test cases." in lint_ctx.warn_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_tests_data_source(lint_ctx):
|
|
tool_source = get_xml_tool_source(TESTS_ABSENT_DATA_SOURCE)
|
|
run_lint(lint_ctx, tests.lint_tests, tool_source)
|
|
assert "No tests found, that should be OK for data_sources." in lint_ctx.info_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_tests_param_output_names(lint_ctx):
|
|
tool_source = get_xml_tool_source(TESTS_PARAM_OUTPUT_NAMES)
|
|
run_lint(lint_ctx, tests.lint_tests, tool_source)
|
|
assert "1 test(s) found." in lint_ctx.valid_messages
|
|
assert "Test 1: Found test param tag without a name defined." in lint_ctx.error_messages
|
|
assert "Test 1: Test param non_existent_test_name not found in the inputs" in lint_ctx.error_messages
|
|
assert "Test 1: Found output tag without a name defined." in lint_ctx.error_messages
|
|
assert (
|
|
"Test 1: Found output tag with unknown name [nonexistent_output], valid names ['existent_output', 'existent_collection']"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert "Test 1: Found output_collection tag without a name defined." in lint_ctx.error_messages
|
|
assert (
|
|
"Test 1: Found output_collection tag with unknown name [nonexistent_collection], valid names ['existent_output', 'existent_collection']"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert not lint_ctx.info_messages
|
|
assert len(lint_ctx.valid_messages) == 1
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 6
|
|
|
|
|
|
def test_tests_expect_failure_output(lint_ctx):
|
|
tool_source = get_xml_tool_source(TESTS_EXPECT_FAILURE_OUTPUT)
|
|
run_lint(lint_ctx, tests.lint_tests, tool_source)
|
|
assert "No valid test(s) found." in lint_ctx.warn_messages
|
|
assert "Test 1: Cannot specify outputs in a test expecting failure." in lint_ctx.error_messages
|
|
assert (
|
|
"Test 2: Cannot make assumptions on the number of outputs in a test expecting failure."
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert len(lint_ctx.error_messages) == 2
|
|
|
|
|
|
def test_tests_without_expectations(lint_ctx):
|
|
tool_source = get_xml_tool_source(TESTS_WO_EXPECTATIONS)
|
|
run_lint(lint_ctx, tests.lint_tests, tool_source)
|
|
assert (
|
|
"Test 1: No outputs or expectations defined for tests, this test is likely invalid." in lint_ctx.warn_messages
|
|
)
|
|
assert "No valid test(s) found." in lint_ctx.warn_messages
|
|
assert not lint_ctx.info_messages
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 2
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_tests_valid(lint_ctx):
|
|
tool_source = get_xml_tool_source(TESTS_VALID)
|
|
run_lint(lint_ctx, tests.lint_tests, tool_source)
|
|
assert "1 test(s) found." in lint_ctx.valid_messages
|
|
assert not lint_ctx.info_messages
|
|
assert len(lint_ctx.valid_messages) == 1
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_tests_asserts(lint_ctx):
|
|
tool_source = get_xml_tool_source(ASSERTS)
|
|
run_lint(lint_ctx, tests.lint_tests, tool_source)
|
|
assert "Test 1: unknown assertion 'invalid'" in lint_ctx.error_messages
|
|
assert "Test 1: unknown attribute 'invalid_attrib' for 'has_text'" in lint_ctx.error_messages
|
|
assert "Test 1: missing attribute 'text' for 'has_text'" in lint_ctx.error_messages
|
|
assert "Test 1: attribute 'value' for 'has_size' needs to be 'int' got '500k'" in lint_ctx.error_messages
|
|
assert "Test 1: attribute 'delta' for 'has_size' needs to be 'int' got '1O'" in lint_ctx.error_messages
|
|
assert (
|
|
"Test 1: unknown attribute 'invalid_attrib_also_checked_in_nested_asserts' for 'not_has_text'"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert "Test 1: 'has_size' needs to specify 'n', 'min', or 'max'" in lint_ctx.error_messages
|
|
assert "Test 1: 'has_n_columns' needs to specify 'n', 'min', or 'max'" in lint_ctx.error_messages
|
|
assert "Test 1: 'has_n_lines' needs to specify 'n', 'min', or 'max'" in lint_ctx.error_messages
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 9
|
|
|
|
|
|
def test_tests_output_type_mismatch(lint_ctx):
|
|
tool_source = get_xml_tool_source(TESTS_OUTPUT_TYPE_MISMATCH)
|
|
run_lint(lint_ctx, tests.lint_tests, tool_source)
|
|
assert (
|
|
"Test 1: test output collection_name does not correspond to a 'data' output, but a 'collection'"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Test 1: test collection output 'data_name' does not correspond to a 'output_collection' output, but a 'data'"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 2
|
|
|
|
|
|
def test_tests_discover_outputs(lint_ctx):
|
|
tool_source = get_xml_tool_source(TESTS_DISCOVER_OUTPUTS)
|
|
run_lint(lint_ctx, tests.lint_tests, tool_source)
|
|
assert (
|
|
"Test 3: test output 'data_name' must have a 'count' attribute and/or 'discovered_dataset' children"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Test 3: test collection 'collection_name' must have a 'count' attribute or 'element' children"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Test 3: test collection 'collection_name' must contain nested 'element' tags and/or element childen with a 'count' attribute"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
"Test 4: test collection 'collection_name' must contain nested 'element' tags and/or element childen with a 'count' attribute"
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 4
|
|
|
|
|
|
def test_tests_expect_num_outputs_filter(lint_ctx):
|
|
tool_source = get_xml_tool_source(TESTS_EXPECT_NUM_OUTPUTS_FILTER)
|
|
run_lint(lint_ctx, tests.lint_tests, tool_source)
|
|
assert "Test should specify 'expect_num_outputs' if outputs have filters" in lint_ctx.warn_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert len(lint_ctx.error_messages) == 0
|
|
|
|
|
|
def test_tests_compare_attrib_incompatibility(lint_ctx):
|
|
tool_source = get_xml_tool_source(TESTS_COMPARE_ATTRIB_INCOMPATIBILITY)
|
|
run_lint(lint_ctx, tests.lint_tsts, tool_source)
|
|
assert (
|
|
'Test 1: Attribute decompress is incompatible with compare="re_match".'
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert (
|
|
'Test 1: Attribute sort is incompatible with compare="contains".'
|
|
in lint_ctx.error_messages
|
|
)
|
|
assert not lint_ctx.info_messages
|
|
assert len(lint_ctx.valid_messages) == 1
|
|
assert not lint_ctx.warn_messages
|
|
assert len(lint_ctx.error_messages) == 2
|
|
|
|
|
|
def test_xml_order(lint_ctx):
|
|
tool_source = get_xml_tool_source(XML_ORDER)
|
|
run_lint(lint_ctx, xml_order.lint_xml_order, tool_source)
|
|
assert "Unknown tag [wrong_tag] encountered, this may result in a warning in the future." in lint_ctx.info_messages
|
|
assert "Best practice violation [stdio] elements should come before [command]" in lint_ctx.warn_messages
|
|
assert len(lint_ctx.info_messages) == 1
|
|
assert not lint_ctx.valid_messages
|
|
assert len(lint_ctx.warn_messages) == 1
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
COMPLETE = """<tool>
|
|
<macros>
|
|
<import>macros.xml</import>
|
|
<xml name="test_macro">
|
|
<param name="select" type="select">
|
|
<option value="a">a</option>
|
|
<option value="a">a</option>
|
|
</param>
|
|
</xml>
|
|
</macros>
|
|
<inputs>
|
|
<expand macro="test_macro"/>
|
|
<param/>
|
|
<expand macro="test_macro2"/>
|
|
</inputs>
|
|
</tool>
|
|
"""
|
|
|
|
COMPLETE_MACROS = """<macros>
|
|
<xml name="test_macro2">
|
|
<param name="No_type"/>
|
|
</xml>
|
|
</macros>
|
|
"""
|
|
|
|
|
|
def test_tool_and_macro_xml(lint_ctx_xpath, lint_ctx):
|
|
"""
|
|
test linters (all of them via lint_tool_source_with) on a tool and macro xml file
|
|
checking a list of asserts, where each assert is a 4-tuple:
|
|
- expected message
|
|
- filename suffix (tool.xml or macro.xml)
|
|
- line number (1 based)
|
|
- xpath
|
|
"""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
tool_path = os.path.join(tmp, "tool.xml")
|
|
macros_path = os.path.join(tmp, "macros.xml")
|
|
with open(tool_path, "w") as tmpf:
|
|
tmpf.write(COMPLETE)
|
|
with open(macros_path, "w") as tmpf:
|
|
tmpf.write(COMPLETE_MACROS)
|
|
tool_xml, _ = load_with_references(tool_path)
|
|
|
|
tool_source = XmlToolSource(tool_xml)
|
|
# lint once with the lint context using XMLLintMessageXPath and XMLLintMessageLine
|
|
lint_tool_source_with(lint_ctx_xpath, tool_source)
|
|
lint_tool_source_with(lint_ctx, tool_source)
|
|
|
|
asserts = (
|
|
("Select parameter [select] has multiple options with the same value", 5, "/tool/inputs/param[1]"),
|
|
("Found param input with no name specified.", 13, "/tool/inputs/param[2]"),
|
|
("Param input [No_type] input with no type specified.", 3, "/tool/inputs/param[3]"),
|
|
)
|
|
for a in asserts:
|
|
message, line, xpath = a
|
|
found = False
|
|
for lint_message in lint_ctx_xpath.message_list:
|
|
if lint_message.message != message:
|
|
continue
|
|
found = True
|
|
assert (
|
|
lint_message.xpath == xpath
|
|
), f"Assumed xpath {xpath}; found xpath {lint_message.xpath} for: {message}"
|
|
assert found, f"Did not find {message}"
|
|
for lint_message in lint_ctx.message_list:
|
|
if lint_message.message != message:
|
|
continue
|
|
found = True
|
|
assert lint_message.line == line, f"Assumed line {line}; found line {lint_message.line} for: {message}"
|
|
assert found, f"Did not find {message}"
|
|
|
|
|
|
# TODO COPIED from test/unit/app/tools/test_tool_deserialization.py
|
|
CWL_TOOL = """
|
|
cwlVersion: v1.0
|
|
class: CommandLineTool
|
|
baseCommand: echo
|
|
inputs:
|
|
message:
|
|
type: string
|
|
inputBinding:
|
|
position: 1
|
|
outputs: []
|
|
"""
|
|
|
|
YAML_TOOL = """
|
|
class: GalaxyTool
|
|
id: simple_constructs_y
|
|
name: simple_constructs_y
|
|
version: 1.0
|
|
command:
|
|
>
|
|
echo "$booltest" >> $out_file1;
|
|
inputs:
|
|
- name: booltest
|
|
type: boolean
|
|
truevalue: booltrue
|
|
falsevalue: boolfalse
|
|
checked: false
|
|
outputs:
|
|
out_file1:
|
|
format: txt
|
|
"""
|
|
|
|
|
|
def test_linting_yml_tool(lint_ctx):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
tool_path = os.path.join(tmp, "tool.yml")
|
|
with open(tool_path, "w") as tmpf:
|
|
tmpf.write(YAML_TOOL)
|
|
tool_sources = load_tool_sources_from_path(tmp)
|
|
assert len(tool_sources) == 1, "Expected 1 tool source"
|
|
tool_source = tool_sources[0][1]
|
|
lint_tool_source_with(lint_ctx, tool_source)
|
|
assert "Tool defines a version [1.0]." in lint_ctx.valid_messages
|
|
assert "Tool defines a name [simple_constructs_y]." in lint_ctx.valid_messages
|
|
assert "Tool defines an id [simple_constructs_y]." in lint_ctx.valid_messages
|
|
assert "Tool specifies profile version [16.04]." in lint_ctx.valid_messages
|
|
assert not lint_ctx.info_messages
|
|
assert len(lint_ctx.valid_messages) == 4
|
|
assert not lint_ctx.warn_messages
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_linting_cwl_tool(lint_ctx):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
tool_path = os.path.join(tmp, "tool.cwl")
|
|
with open(tool_path, "w") as tmpf:
|
|
tmpf.write(CWL_TOOL)
|
|
tool_sources = load_tool_sources_from_path(tmp)
|
|
assert len(tool_sources) == 1, "Expected 1 tool source"
|
|
tool_source = tool_sources[0][1]
|
|
lint_tool_source_with(lint_ctx, tool_source)
|
|
assert "Tool defines a version [0.0.1]." in lint_ctx.valid_messages
|
|
assert "Tool defines a name [tool.cwl]." in lint_ctx.valid_messages
|
|
assert "Tool defines an id [tool.cwl]." in lint_ctx.valid_messages
|
|
assert "Tool specifies profile version [17.09]." in lint_ctx.valid_messages
|
|
assert "CWL appears to be valid." in lint_ctx.info_messages
|
|
assert "Description of tool is empty or absent." in lint_ctx.warn_messages
|
|
assert "Tool does not specify a DockerPull source." in lint_ctx.warn_messages
|
|
assert "Modern CWL version [v1.0]." in lint_ctx.info_messages
|
|
assert len(lint_ctx.info_messages) == 2
|
|
assert len(lint_ctx.valid_messages) == 4
|
|
assert len(lint_ctx.warn_messages) == 2
|
|
assert not lint_ctx.error_messages
|
|
|
|
|
|
def test_xml_comments_are_ignored(lint_ctx: LintContext):
|
|
tool_xml = get_tool_xml_exact(TOOL_WITH_COMMENTS)
|
|
lint_xml_with(lint_ctx, tool_xml)
|
|
for lint_message in lint_ctx.message_list:
|
|
assert "Comment" not in lint_message.message
|