Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion lib/galaxy/tool_util/verify/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
tifffile = None # type: ignore[assignment, unused-ignore]


from packaging.version import Version

from galaxy.tool_util.parser.util import (
DEFAULT_DELTA,
DEFAULT_DELTA_FRAC,
Expand Down Expand Up @@ -75,6 +77,7 @@ def verify(
keep_outputs_dir: Optional[str] = None,
verify_extra_files: Optional[Callable] = None,
mode="file",
profile: Optional[str] = None,
):
"""Verify the content of a test output using test definitions described by attributes.

Expand All @@ -99,7 +102,11 @@ def get_filename(filename: str) -> str:
assertions = attributes.get("assert_list", None)
if assertions is not None:
try:
verify_assertions(output_content, attributes["assert_list"], attributes.get("decompress", False))
# Auto-detect separator based on file type for profile >= 26.0
sep: Optional[str] = None
if profile and Version(profile) >= Version("26.0"):
sep = "," if attributes.get("ftype") == "csv" else "\t"
verify_assertions(output_content, attributes["assert_list"], attributes.get("decompress", False), sep=sep)
except AssertionError as err:
errmsg = f"{item_label} different than expected\n"
errmsg += unicodify(err)
Expand Down
1 change: 1 addition & 0 deletions lib/galaxy/tool_util/verify/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
class ToolTestDescriptionDict(TypedDict):
tool_id: str
tool_version: Optional[str]
profile: NotRequired[Optional[str]]
name: str
test_index: int
inputs: ExpandedToolInputsJsonified
Expand Down
13 changes: 10 additions & 3 deletions lib/galaxy/tool_util/verify/asserts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import (
Callable,
Dict,
Optional,
Tuple,
)

Expand Down Expand Up @@ -41,7 +42,9 @@
assertion_functions: Dict[str, Callable] = {k: v[1] for (k, v) in assertion_module_and_functions.items()}


def verify_assertions(data: bytes, assertion_description_list: list, decompress: bool = False):
def verify_assertions(
data: bytes, assertion_description_list: list, decompress: bool = False, sep: Optional[str] = None
):
"""This function takes a list of assertions and a string to check
these assertions against."""
if decompress:
Expand All @@ -51,10 +54,10 @@ def verify_assertions(data: bytes, assertion_description_list: list, decompress:
with get_fileobj(tmpfh.name, mode="rb", compressed_formats=None) as fh:
data = fh.read()
for assertion_description in assertion_description_list:
verify_assertion(data, assertion_description)
verify_assertion(data, assertion_description, sep=sep)


def verify_assertion(data: bytes, assertion_description):
def verify_assertion(data: bytes, assertion_description, sep: Optional[str] = None):
tag = assertion_description["tag"]
assert_function_name = "assert_" + tag
assert_function = assertion_functions.get(assert_function_name)
Expand Down Expand Up @@ -103,5 +106,9 @@ def verify_assertion(data: bytes, assertion_description):
if "children" in assert_function_args:
args["children"] = assertion_description["children"]

# Only set sep if the assertion accepts it and it's not already specified in XML
if "sep" in assert_function_args and sep is not None and "sep" not in assertion_description["attributes"]:
args["sep"] = sep

# TODO: Verify all needed function arguments are specified.
assert_function(**args)
9 changes: 6 additions & 3 deletions lib/galaxy/tool_util/verify/asserts/tabular.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
)
from ._util import _assert_number

Sep = Annotated[str, AssertionParameter("Separator defining columns, default: tab")]
Sep = Annotated[
str, AssertionParameter("Separator defining columns, default: tab (or comma for csv with profile >= 26.0)")
]
Comment = Annotated[
str,
AssertionParameter(
Expand Down Expand Up @@ -53,9 +55,10 @@ def assert_has_n_columns(
Number of columns can optionally also be specified with ``delta``. Alternatively the
range of expected occurences can be specified by ``min`` and/or ``max``.

Optionally a column separator (``sep``, default is ``\t``) `and comment character(s)
Optionally a column separator (``sep``) and comment character(s)
can be specified (``comment``, default is empty string). The first non-comment
line is used for determining the number of columns.
line is used for determining the number of columns. For tools with profile >= 26.0,
the default separator is tab for most tabular data types, but comma for csv files.
"""
first_line = get_first_line(output, comment)
n_columns = len(first_line.split(sep))
Expand Down
42 changes: 38 additions & 4 deletions lib/galaxy/tool_util/verify/interactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,15 @@ class ValidToolTestDict(TypedDict):
error: Literal[False]
tool_id: str
tool_version: str
profile: NotRequired[Optional[str]]
test_index: int


class InvalidToolTestDict(TypedDict):
error: Literal[True]
tool_id: str
tool_version: str
profile: NotRequired[Optional[str]]
test_index: int
inputs: Any
exception: str
Expand Down Expand Up @@ -303,7 +305,13 @@ def get_tool_tests(self, tool_id: str, tool_version: Optional[str] = None) -> Li
return response.json()

def verify_output_collection(
self, output_collection_def, output_collection_id, history, tool_id, tool_version=None
self,
output_collection_def,
output_collection_id,
history,
tool_id,
tool_version=None,
profile: Optional[str] = None,
):
data_collection = self._get(
f"dataset_collections/{output_collection_id}", data={"instance_type": "history"}
Expand All @@ -319,6 +327,7 @@ def verify_dataset(element, element_attrib, element_outfile):
attributes=element_attrib,
tool_id=tool_id,
tool_version=tool_version,
profile=profile,
)
except AssertionError as e:
raise AssertionError(
Expand All @@ -327,7 +336,17 @@ def verify_dataset(element, element_attrib, element_outfile):

verify_collection(output_collection_def, data_collection, verify_dataset)

def verify_output(self, history_id, jobs, output_data, output_testdef, tool_id, maxseconds, tool_version=None):
def verify_output(
self,
history_id,
jobs,
output_data,
output_testdef,
tool_id,
maxseconds,
tool_version=None,
profile: Optional[str] = None,
):
outfile = output_testdef.outfile
attributes = output_testdef.attributes
name = output_testdef.name
Expand All @@ -342,6 +361,7 @@ def verify_output(self, history_id, jobs, output_data, output_testdef, tool_id,
attributes=attributes,
tool_id=tool_id,
tool_version=tool_version,
profile=profile,
)
except AssertionError as e:
raise AssertionError(f"Output {name}: {str(e)}")
Expand Down Expand Up @@ -378,6 +398,7 @@ def verify_output(self, history_id, jobs, output_data, output_testdef, tool_id,
primary_attributes,
tool_id=tool_id,
tool_version=tool_version,
profile=profile,
)
except AssertionError as e:
raise AssertionError(f"Primary output {name}: {str(e)}")
Expand All @@ -386,7 +407,9 @@ def wait_for_jobs(self, history_id, jobs, maxseconds):
for job in jobs:
self.wait_for_job(job["id"], history_id, maxseconds)

def verify_output_dataset(self, history_id, hda_id, outfile, attributes, tool_id, tool_version=None):
def verify_output_dataset(
self, history_id, hda_id, outfile, attributes, tool_id, tool_version=None, profile: Optional[str] = None
):
fetcher = self.__dataset_fetcher(history_id)
test_data_downloader = self.__test_data_downloader(tool_id, tool_version, attributes)
verify_hid(
Expand All @@ -396,6 +419,7 @@ def verify_output_dataset(self, history_id, hda_id, outfile, attributes, tool_id
dataset_fetcher=fetcher,
test_data_downloader=test_data_downloader,
keep_outputs_dir=self.keep_outputs_dir,
profile=profile,
)
self._verify_metadata(history_id, hda_id, attributes)

Expand Down Expand Up @@ -1300,6 +1324,7 @@ def verify_hid(
test_data_downloader,
dataset_fetcher=None,
keep_outputs_dir: Optional[str] = None,
profile: Optional[str] = None,
):
assert dataset_fetcher is not None

Expand All @@ -1322,6 +1347,7 @@ def verify_extra_files(extra_files):
get_filecontent=test_data_downloader,
keep_outputs_dir=keep_outputs_dir,
verify_extra_files=verify_extra_files,
profile=profile,
)


Expand Down Expand Up @@ -1757,6 +1783,7 @@ def register_exception(e: Exception):
tool_id=job["tool_id"],
maxseconds=maxseconds,
tool_version=testdef.tool_version,
profile=testdef.profile,
)
except Exception as e:
register_exception(e)
Expand Down Expand Up @@ -1816,7 +1843,11 @@ def register_exception(e: Exception):
# the job completed so re-hit the API for more information.
data_collection_id = data_collection_list[name]["id"]
galaxy_interactor.verify_output_collection(
output_collection_def, data_collection_id, history, job["tool_id"]
output_collection_def,
data_collection_id,
history,
job["tool_id"],
profile=testdef.profile,
)
except Exception as e:
register_exception(e)
Expand Down Expand Up @@ -1993,6 +2024,7 @@ class ToolTestDescription:
name: str
tool_id: str
tool_version: Optional[str]
profile: Optional[str]
test_index: int
num_outputs: Optional[int]
stdout: Optional[AssertionList]
Expand Down Expand Up @@ -2041,6 +2073,7 @@ def __init__(self, json_dict: ToolTestDescriptionDict):
self.request_schema = json_dict.get("request_schema", None)
self.tool_id = json_dict["tool_id"]
self.tool_version = json_dict.get("tool_version")
self.profile = json_dict.get("profile")
self.maxseconds = json_dict.get("maxseconds")

def test_data(self):
Expand All @@ -2067,6 +2100,7 @@ def to_dict(self) -> ToolTestDescriptionDict:
"test_index": self.test_index,
"tool_id": self.tool_id,
"tool_version": self.tool_version,
"profile": self.profile,
"required_files": self.required_files,
"required_data_tables": self.required_data_tables,
"required_loc_files": self.required_loc_files,
Expand Down
3 changes: 3 additions & 0 deletions lib/galaxy/tool_util/verify/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ def _description_from_tool_source(

tool_id, tool_version = _tool_id_and_version(tool_source, tool_guid)
processed_test_dict: Union[ValidToolTestDict, InvalidToolTestDict]
profile = tool_source.parse_profile()
try:
processed_inputs = _process_raw_inputs(
tool_source,
Expand Down Expand Up @@ -164,6 +165,7 @@ def _description_from_tool_source(
"required_loc_files": required_loc_files,
"tool_id": tool_id,
"tool_version": tool_version,
"profile": profile,
"test_index": test_index,
"maxseconds": maxseconds,
"error": False,
Expand All @@ -174,6 +176,7 @@ def _description_from_tool_source(
{
"tool_id": tool_id,
"tool_version": tool_version,
"profile": profile,
"test_index": test_index,
"inputs": {},
"error": True,
Expand Down
7 changes: 4 additions & 3 deletions lib/galaxy/tool_util/xsd/galaxy.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -2606,9 +2606,10 @@ For instance, ``<has_n_columns n="3"/>``. The assertion tests only the first lin
Number of columns can optionally also be specified with ``delta``. Alternatively the
range of expected occurences can be specified by ``min`` and/or ``max``.

Optionally a column separator (``sep``, default is `` ``) `and comment character(s)
Optionally a column separator (``sep``) and comment character(s)
can be specified (``comment``, default is empty string). The first non-comment
line is used for determining the number of columns.
line is used for determining the number of columns. For tools with profile >= 26.0,
the default separator is tab for most tabular data types, but comma for csv files.

$attribute_list::5]]></xs:documentation>
</xs:annotation>
Expand All @@ -2635,7 +2636,7 @@ $attribute_list::5]]></xs:documentation>
</xs:attribute>
<xs:attribute name="sep" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation xml:lang="en"><![CDATA[Separator defining columns, default: tab]]></xs:documentation>
<xs:documentation xml:lang="en"><![CDATA[Separator defining columns, default: tab (or comma for csv with profile >= 26.0)]]></xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="comment" type="xs:string" use="optional">
Expand Down
Loading
Loading