diff --git a/lib/galaxy/tool_util/linters/tests.py b/lib/galaxy/tool_util/linters/tests.py index 97de651b65bd..43f409edf401 100644 --- a/lib/galaxy/tool_util/linters/tests.py +++ b/lib/galaxy/tool_util/linters/tests.py @@ -85,6 +85,35 @@ def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): class TestsAssertsHasSizeQuant(Linter): + """ + has_size needs at least one of size (or value), min, max + """ + + @classmethod + def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): + tool_xml = getattr(tool_source, "xml_tree", None) + if not tool_xml: + return + tests = tool_xml.findall("./tests/test") + for test_idx, test in enumerate(tests, start=1): + for a in test.xpath( + ".//*[self::assert_contents or self::assert_stdout or self::assert_stderr or self::assert_command]//*" + ): + if a.tag != "has_size": + continue + if len(set(a.attrib) & set(["value", "size", "min", "max"])) == 0: + lint_ctx.error( + f"Test {test_idx}: '{a.tag}' needs to specify 'size', 'min', or 'max'", + linter=cls.name(), + node=a, + ) + + +class TestsAssertsHasSizeOrValueQuant(Linter): + """ + has_size should not have size and value + """ + @classmethod def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): tool_xml = getattr(tool_source, "xml_tree", None) @@ -97,9 +126,9 @@ def lint(cls, tool_source: "ToolSource", lint_ctx: "LintContext"): ): if a.tag != "has_size": continue - if len(set(a.attrib) & set(["value", "min", "max"])) == 0: + if "value" in a.attrib and "size" in a.attrib: lint_ctx.error( - f"Test {test_idx}: '{a.tag}' needs to specify 'value', 'min', or 'max'", + f"Test {test_idx}: '{a.tag}' must not specify 'value' and 'size'", linter=cls.name(), node=a, ) diff --git a/lib/galaxy/tool_util/verify/asserts/size.py b/lib/galaxy/tool_util/verify/asserts/size.py index de20309f99ed..6eb6cb43094d 100644 --- a/lib/galaxy/tool_util/verify/asserts/size.py +++ b/lib/galaxy/tool_util/verify/asserts/size.py @@ -9,16 +9,20 @@ def assert_has_size( output_bytes: bytes, value: Optional[Union[int, str]] = None, + size: Optional[Union[int, str]] = None, delta: Union[int, str] = 0, min: Optional[Union[int, str]] = None, max: Optional[Union[int, str]] = None, negate: Union[bool, str] = False, ) -> None: """ - Asserts the specified output has a size of the specified value, + Asserts the specified output has a size of the specified value + (size and value or synonyms), allowing for absolute (delta) and relative (delta_frac) difference. """ output_size = len(output_bytes) + if size is None: + size = value _assert_number( output_size, value, diff --git a/lib/galaxy/tool_util/xsd/galaxy.xsd b/lib/galaxy/tool_util/xsd/galaxy.xsd index 224949fdbd7a..27cafa7f3bc2 100644 --- a/lib/galaxy/tool_util/xsd/galaxy.xsd +++ b/lib/galaxy/tool_util/xsd/galaxy.xsd @@ -2252,11 +2252,16 @@ $attribute_list::5 ]]> - + Desired size of the output (in bytes), can be suffixed by ``(k|M|G|T|P|E)i?`` + + + An outdated alias for `size` + + Maximum allowed size difference (default is 0). The observed size has to be in the range ``value +- delta``. Can be suffixed by ``(k|M|G|T|P|E)i?`` diff --git a/test/unit/tool_util/test_tool_linters.py b/test/unit/tool_util/test_tool_linters.py index 0600ae32c4f2..e553d2c16884 100644 --- a/test/unit/tool_util/test_tool_linters.py +++ b/test/unit/tool_util/test_tool_linters.py @@ -748,6 +748,7 @@ + @@ -1799,11 +1800,12 @@ def test_tests_asserts(lint_ctx): "Invalid XML: Element 'not_has_text', attribute 'invalid_attrib_also_checked_in_nested_asserts': The attribute 'invalid_attrib_also_checked_in_nested_asserts' is not allowed." in lint_ctx.error_messages ) - assert "Test 1: 'has_size' needs to specify 'value', 'min', or 'max'" in lint_ctx.error_messages + assert "Test 1: 'has_size' needs to specify 'size', 'min', or 'max'" in lint_ctx.error_messages + assert "Test 1: 'has_size' must not specify 'value' and 'size'" 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) == 8 + assert len(lint_ctx.error_messages) == 9 def test_tests_output_type_mismatch(lint_ctx): @@ -2076,7 +2078,7 @@ def test_xml_comments_are_ignored(lint_ctx: LintContext): def test_list_linters(): linter_names = Linter.list_listers() # make sure to add/remove a test for new/removed linters if this number changes - assert len(linter_names) == 129 + assert len(linter_names) == 130 assert "Linter" not in linter_names # make sure that linters from all modules are available for prefix in [ diff --git a/test/unit/tool_util/verify/test_asserts.py b/test/unit/tool_util/verify/test_asserts.py index 22d5b67b23f7..a7f419fa4a32 100644 --- a/test/unit/tool_util/verify/test_asserts.py +++ b/test/unit/tool_util/verify/test_asserts.py @@ -472,12 +472,12 @@ def test_has_line_matching_n_failure(): SIZE_HAS_SIZE_ASSERTION = """ - + """ SIZE_HAS_SIZE_ASSERTION_DELTA = """ - + """ @@ -491,39 +491,45 @@ def test_has_line_matching_n_failure(): def test_has_size_success(): """test has_size""" - a = run_assertions(SIZE_HAS_SIZE_ASSERTION.format(value=10), TEXT_DATA_HAS_TEXT) + a = run_assertions(SIZE_HAS_SIZE_ASSERTION.format(size_attrib="size", value=10), TEXT_DATA_HAS_TEXT) assert len(a) == 0 def test_has_size_failure(): """test has_size .. negative test""" - a = run_assertions(SIZE_HAS_SIZE_ASSERTION.format(value="10"), TEXT_DATA_HAS_TEXT * 2) + a = run_assertions(SIZE_HAS_SIZE_ASSERTION.format(size_attrib="value", value="10"), TEXT_DATA_HAS_TEXT * 2) assert "Expected file size of 10+-0 found 20" in a assert len(a) == 1 def test_has_size_delta(): """test has_size .. delta""" - a = run_assertions(SIZE_HAS_SIZE_ASSERTION_DELTA.format(value="10", delta="10"), TEXT_DATA_HAS_TEXT * 2) + a = run_assertions( + SIZE_HAS_SIZE_ASSERTION_DELTA.format(size_attrib="size", value="10", delta="10"), TEXT_DATA_HAS_TEXT * 2 + ) assert len(a) == 0 def test_has_size_with_bytes_suffix(): """test has_size .. bytes suffix""" - a = run_assertions(SIZE_HAS_SIZE_ASSERTION_DELTA.format(value="1k", delta="0"), TEXT_DATA_HAS_TEXT * 100) + a = run_assertions( + SIZE_HAS_SIZE_ASSERTION_DELTA.format(size_attrib="size", value="1k", delta="0"), TEXT_DATA_HAS_TEXT * 100 + ) assert len(a) == 0 def test_has_size_with_bytes_suffix_failure(): """test has_size .. bytes suffix .. negative""" - a = run_assertions(SIZE_HAS_SIZE_ASSERTION_DELTA.format(value="1Mi", delta="10k"), TEXT_DATA_HAS_TEXT * 100) + a = run_assertions( + SIZE_HAS_SIZE_ASSERTION_DELTA.format(size_attrib="value", value="1Mi", delta="10k"), TEXT_DATA_HAS_TEXT * 100 + ) assert "Expected file size of 1Mi+-10k found 1000" in a assert len(a) == 1 def test_has_size_decompress_gz(): """test has_size with gzipped data using decompress=True (which in real life is set int he parent output tag)""" - a = run_assertions(SIZE_HAS_SIZE_ASSERTION.format(value="100"), GZA100, decompress=True) + a = run_assertions(SIZE_HAS_SIZE_ASSERTION.format(size_attrib="size", value="100"), GZA100, decompress=True) assert len(a) == 0 @@ -532,7 +538,7 @@ def test_has_size_decompress_txt(): test has_size with NON-gzipped data using decompress=True -> decompress should be ignored - in particular there should be no error """ - a = run_assertions(SIZE_HAS_SIZE_ASSERTION.format(value="100"), A100, decompress=True) + a = run_assertions(SIZE_HAS_SIZE_ASSERTION.format(size_attrib="size", value="100"), A100, decompress=True) assert len(a) == 0