mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #12528 from bernt-matthias/topic/n-col-assert
Various extensions and improvements for asserts
This commit is contained in:
+37
-18
@@ -90,28 +90,32 @@ def _build_tag(tag, hide_attributes):
|
||||
tag_help = StringIO()
|
||||
annotation_el = tag_el.find("{http://www.w3.org/2001/XMLSchema}annotation")
|
||||
text = annotation_el.find("{http://www.w3.org/2001/XMLSchema}documentation").text
|
||||
text = _replace_attribute_list(tag, text, attributes)
|
||||
for line in text.splitlines():
|
||||
if line.startswith("$attribute_list:"):
|
||||
attributes_str, header_level = line.split(":")[1:3]
|
||||
attribute_names = attributes_str.split(",")
|
||||
header_level = int(header_level)
|
||||
text = text.replace(line, _build_attributes_table(tag, attributes, attribute_names=attribute_names, header_level=header_level))
|
||||
if line.startswith("$assertions"):
|
||||
assertions_tag = xmlschema_doc.find("//{http://www.w3.org/2001/XMLSchema}complexType[@name='TestAssertions']")
|
||||
assertion_tag = xmlschema_doc.find("//{http://www.w3.org/2001/XMLSchema}group[@name='TestAssertion']")
|
||||
assertions_buffer = StringIO()
|
||||
assertions_buffer.write(_doc_or_none(assertions_tag))
|
||||
assertions_buffer.write("\n\n")
|
||||
assertions_buffer.write("Child Element/Assertion | Details \n")
|
||||
assertions_buffer.write("--- | ---\n")
|
||||
elements = assertion_tag.findall("{http://www.w3.org/2001/XMLSchema}choice/{http://www.w3.org/2001/XMLSchema}element")
|
||||
for element in elements:
|
||||
doc = _doc_or_none(element)
|
||||
if doc is None:
|
||||
doc = _doc_or_none(_type_el(element))
|
||||
assert doc is not None, "Documentation for %s is empty" % element.attrib["name"]
|
||||
doc = doc.strip()
|
||||
assertions_buffer.write("``{}`` | {}\n".format(element.attrib["name"], doc))
|
||||
|
||||
assertion_groups = assertions_tag.xpath("xs:choice/xs:group", namespaces={'xs': 'http://www.w3.org/2001/XMLSchema'})
|
||||
for group in assertion_groups:
|
||||
ref = group.attrib['ref']
|
||||
assertion_tag = xmlschema_doc.find("//{http://www.w3.org/2001/XMLSchema}group[@name='" + ref + "']")
|
||||
doc = _doc_or_none(assertion_tag)
|
||||
assertions_buffer.write(f"### {doc}\n\n")
|
||||
elements = assertion_tag.findall("{http://www.w3.org/2001/XMLSchema}choice/{http://www.w3.org/2001/XMLSchema}element")
|
||||
for element in elements:
|
||||
doc = _doc_or_none(element)
|
||||
if doc is None:
|
||||
doc = _doc_or_none(_type_el(element))
|
||||
assert doc is not None, "Documentation for %s is empty" % element.attrib["name"]
|
||||
doc = doc.strip()
|
||||
|
||||
element_el = _find_tag_el(element)
|
||||
element_attributes = _find_attributes(element_el)
|
||||
doc = _replace_attribute_list(element_el, doc, element_attributes)
|
||||
assertions_buffer.write(f"#### ``{element.attrib['name']}``:\n\n{doc}\n\n")
|
||||
text = text.replace(line, assertions_buffer.getvalue())
|
||||
tag_help.write(text)
|
||||
best_practices = _get_bp_link(annotation_el)
|
||||
@@ -125,6 +129,20 @@ element [here](%s).""" % best_practices)
|
||||
return tag_help.getvalue()
|
||||
|
||||
|
||||
def _replace_attribute_list(tag, text, attributes):
|
||||
for line in text.splitlines():
|
||||
if not line.startswith("$attribute_list:"):
|
||||
continue
|
||||
attributes_str, header_level = line.split(":")[1:3]
|
||||
if attributes_str == "":
|
||||
attribute_names = None
|
||||
else:
|
||||
attribute_names = attributes_str.split(",")
|
||||
header_level = int(header_level)
|
||||
text = text.replace(line, _build_attributes_table(tag, attributes, attribute_names=attribute_names, header_level=header_level))
|
||||
return text
|
||||
|
||||
|
||||
def _get_bp_link(annotation_el):
|
||||
anchor = annotation_el.attrib.get("{http://galaxyproject.org/xml/1.0}best_practices", None)
|
||||
link = None
|
||||
@@ -190,8 +208,9 @@ def _find_tag_el(tag):
|
||||
|
||||
def _type_el(tag):
|
||||
element_type = tag.attrib["type"]
|
||||
type_el = xmlschema_doc.find("//{http://www.w3.org/2001/XMLSchema}complexType/[@name='%s']" % element_type) or \
|
||||
xmlschema_doc.find("//{http://www.w3.org/2001/XMLSchema}simpleType/[@name='%s']" % element_type)
|
||||
type_el = xmlschema_doc.find("//{http://www.w3.org/2001/XMLSchema}complexType/[@name='%s']" % element_type)
|
||||
if type_el is None:
|
||||
type_el = xmlschema_doc.find("//{http://www.w3.org/2001/XMLSchema}simpleType/[@name='%s']" % element_type)
|
||||
return type_el
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
"""This module contains a linting functions for tool tests."""
|
||||
from inspect import Parameter, signature
|
||||
|
||||
from ._util import is_datasource
|
||||
from ..verify import asserts
|
||||
|
||||
|
||||
# Misspelled so as not be picked up by nosetests.
|
||||
@@ -35,9 +38,14 @@ def lint_tsts(tool_xml, lint_ctx):
|
||||
break
|
||||
test_assert = ("assert_stdout", "assert_stderr", "assert_command")
|
||||
for ta in test_assert:
|
||||
if len(test.findall(ta)) > 0:
|
||||
has_test = True
|
||||
break
|
||||
assertions = test.findall(ta)
|
||||
if len(assertions) == 0:
|
||||
continue
|
||||
if len(assertions) > 1:
|
||||
lint_ctx.error("Test {test_idx}: More than one {ta} found. Only the first is considered.")
|
||||
has_test = True
|
||||
_check_asserts(test_idx, assertions, lint_ctx)
|
||||
_check_asserts(test_idx, test.findall(".//assert_contents"), lint_ctx)
|
||||
|
||||
# really simple test that test parameters are also present in the inputs
|
||||
for param in test.findall("param"):
|
||||
@@ -94,6 +102,45 @@ def lint_tsts(tool_xml, lint_ctx):
|
||||
lint_ctx.warn("No valid test(s) found.", line=tests_line, xpath=tests_path)
|
||||
|
||||
|
||||
def _check_asserts(test_idx, assertions, lint_ctx):
|
||||
"""
|
||||
assertions is a list of assert_contents, assert_stdout, assert_stderr, assert_command
|
||||
in practice only for the first case the list may be longer than one
|
||||
"""
|
||||
for assertion in assertions:
|
||||
for i, a in enumerate(assertion.iter()):
|
||||
if i == 0: # skip root note itself
|
||||
continue
|
||||
assert_function_name = "assert_" + a.tag
|
||||
if assert_function_name not in asserts.assertion_functions:
|
||||
lint_ctx.error(f"Test {test_idx}: unknown assertion {a.tag}")
|
||||
continue
|
||||
assert_function_sig = signature(asserts.assertion_functions[assert_function_name])
|
||||
# check type of the attributes (int, float ...)
|
||||
for attrib in a.attrib:
|
||||
if attrib not in assert_function_sig.parameters:
|
||||
lint_ctx.error(f"Test {test_idx}: unknown attribute {attrib} for {a.tag}")
|
||||
continue
|
||||
if assert_function_sig.parameters[attrib].annotation is not Parameter.empty:
|
||||
try:
|
||||
assert_function_sig.parameters[attrib].annotation(a.attrib[attrib])
|
||||
except ValueError:
|
||||
lint_ctx.error(f"Test {test_idx}: attribute {attrib} for {a.tag} needs to be {assert_function_sig.parameters[attrib].annotation.__name__} got {a.attrib[attrib]}")
|
||||
# check missing required attributes
|
||||
for p in assert_function_sig.parameters:
|
||||
if p in ["output", "output_bytes", "verify_assertions_function", "children"]:
|
||||
continue
|
||||
if assert_function_sig.parameters[p].default is Parameter.empty and p not in a.attrib:
|
||||
lint_ctx.error(f"Test {test_idx}: missing attribute {p} for {a.tag}")
|
||||
# has_n_lines, has_n_columns, and has_size need to specify n/value, min, or max
|
||||
if a.tag in ["has_n_lines", "has_n_columns"]:
|
||||
if "n" not in a.attrib and "min" not in a.attrib and "max" not in a.attrib:
|
||||
lint_ctx.error(f"Test {test_idx}: {a.tag} needs to specify 'n', 'min', or 'max'")
|
||||
if a.tag == "has_size":
|
||||
if "value" not in a.attrib and "min" not in a.attrib and "max" not in a.attrib:
|
||||
lint_ctx.error(f"Test {test_idx}: {a.tag} needs to specify 'n', 'min', or 'max'")
|
||||
|
||||
|
||||
def _collect_output_names(tool_xml):
|
||||
output_data_names = []
|
||||
output_collection_names = []
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
import sys
|
||||
from inspect import getfullargspec
|
||||
from inspect import getfullargspec, getmembers
|
||||
|
||||
from galaxy.util import unicodify
|
||||
|
||||
@@ -12,16 +12,19 @@ assertion_module_names = ['text', 'tabular', 'xml', 'hdf5', 'archive', 'size']
|
||||
# create a new module of assertion functions, create the needed python
|
||||
# source file "test/base/asserts/<MODULE_NAME>.py" and add
|
||||
# <MODULE_NAME> to the list of assertion module names defined above.
|
||||
assertion_modules = []
|
||||
assertion_functions = {}
|
||||
for assertion_module_name in assertion_module_names:
|
||||
full_assertion_module_name = f"galaxy.tool_util.verify.asserts.{assertion_module_name}"
|
||||
try:
|
||||
# Dynamically import module
|
||||
__import__(full_assertion_module_name)
|
||||
assertion_module = sys.modules[full_assertion_module_name]
|
||||
assertion_modules.append(assertion_module)
|
||||
except Exception:
|
||||
log.exception('Failed to load assertion module: %s', assertion_module_name)
|
||||
continue
|
||||
for member, value in getmembers(assertion_module):
|
||||
if member.startswith("assert_"):
|
||||
assertion_functions[member] = value
|
||||
|
||||
|
||||
def verify_assertions(data, assertion_description_list):
|
||||
@@ -33,14 +36,11 @@ def verify_assertions(data, assertion_description_list):
|
||||
|
||||
def verify_assertion(data, assertion_description):
|
||||
tag = assertion_description["tag"]
|
||||
assert_function_name = f"assert_{tag}"
|
||||
assert_function = None
|
||||
for assertion_module in assertion_modules:
|
||||
if hasattr(assertion_module, assert_function_name):
|
||||
assert_function = getattr(assertion_module, assert_function_name)
|
||||
assert_function_name = "assert_" + tag
|
||||
assert_function = assertion_functions.get(assert_function_name)
|
||||
|
||||
if assert_function is None:
|
||||
errmsg = f"Unable to find test function associated with XML tag '{tag}'. Check your tool file syntax."
|
||||
errmsg = f"Unable to find test function associated with XML tag {tag}. Check your tool file syntax."
|
||||
raise AssertionError(errmsg)
|
||||
|
||||
assert_function_args = getfullargspec(assert_function).args
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
from math import inf
|
||||
|
||||
from galaxy.util import asbool
|
||||
from galaxy.util.bytesize import parse_bytesize
|
||||
|
||||
|
||||
def _assert_number(count, n, delta, min, max, negate, n_text, min_max_text):
|
||||
"""
|
||||
helper function for assering that count is in
|
||||
- [n-delta:n+delta]
|
||||
- [min:max]
|
||||
|
||||
raising an assertion error using n_text and min_max_text (resp)
|
||||
substituting {n}, {delta}, {min}, and {max}
|
||||
(and keeping potentially present {text} and {output})
|
||||
|
||||
n, delta, min, max can be suffixed by (K|M|G|T|P|E)i?
|
||||
"""
|
||||
negate = asbool(negate)
|
||||
expected = "Expected" if not negate else "Did not expect"
|
||||
if n is not None:
|
||||
n_bytes = parse_bytesize(n)
|
||||
delta_bytes = parse_bytesize(delta)
|
||||
assert (not negate) == (abs(count - n_bytes) <= delta_bytes), n_text.format(expected=expected, n=n, delta=delta, text="{text}", output="{output}") + f" found {count}"
|
||||
if min is not None or max is not None:
|
||||
if min is None:
|
||||
min = -inf # also replacing min/max for output
|
||||
min_bytes = -inf
|
||||
else:
|
||||
min_bytes = parse_bytesize(min)
|
||||
if max is None:
|
||||
max = inf
|
||||
max_bytes = inf
|
||||
else:
|
||||
max_bytes = parse_bytesize(max)
|
||||
assert (not negate) == (min_bytes <= count <= max_bytes), min_max_text.format(expected=expected, min=min, max=max, text="{text}", output="{output}") + f" found {count}"
|
||||
|
||||
|
||||
def _assert_presence_number(output, text, n, delta, min, max, negate, check_presence_foo, count_foo, presence_text, n_text, min_max_text):
|
||||
"""
|
||||
helper function to assert that
|
||||
- text is present in output using check_presence_foo
|
||||
this is done only if n, min, and max are None
|
||||
- text appears a certain number of times, where the count is determined with count foo
|
||||
|
||||
raising an assertion error using presence_text, n_text or min_max_text (resp)
|
||||
substituting {n}, {delta}, {min}, {max}, {text}, and {output}
|
||||
|
||||
n, delta, min, max can be suffixed by (K|M|G|T|P|E)i?
|
||||
"""
|
||||
negate = asbool(negate)
|
||||
expected = "Expected" if not negate else "Did not expect"
|
||||
if n is None and min is None and max is None:
|
||||
assert (not negate) == check_presence_foo(output, text), presence_text.format(expected=expected, output=output, text=text)
|
||||
try:
|
||||
_assert_number(count_foo(output, text), n, delta, min, max, negate, n_text, min_max_text)
|
||||
except AssertionError as e:
|
||||
raise AssertionError(str(e).format(output=output, text=text))
|
||||
@@ -1,37 +1,90 @@
|
||||
import io
|
||||
import re
|
||||
import tarfile
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
|
||||
def _extract_from_tar(tar_temp, path):
|
||||
for fn in tar_temp.getnames():
|
||||
if re.match(path, fn):
|
||||
# Will only match on first hit, probably fine for now
|
||||
return tar_temp.extractfile(fn)
|
||||
from galaxy.util import asbool
|
||||
from ._util import _assert_presence_number
|
||||
|
||||
|
||||
def _extract_from_zip(zip_temp, path):
|
||||
for fn in zip_temp.namelist():
|
||||
if re.match(path, fn):
|
||||
# Will only match on first hit, probably fine for now
|
||||
return zip_temp.open(fn)
|
||||
def _extract_from_tar(bytes, fn):
|
||||
with io.BytesIO(bytes) as temp:
|
||||
with tarfile.open(fileobj=temp, mode='r') as tar_temp:
|
||||
ti = tar_temp.getmember(fn)
|
||||
# zip treats directories like empty files.
|
||||
# so make this consistent for tar
|
||||
if ti.isdir():
|
||||
return ""
|
||||
with tar_temp.extractfile(fn) as member_fh:
|
||||
return member_fh.read()
|
||||
|
||||
|
||||
def assert_has_archive_member(output_bytes, path, verify_assertions_function, children):
|
||||
def _list_from_tar(bytes, path):
|
||||
lst = list()
|
||||
with io.BytesIO(bytes) as temp:
|
||||
with tarfile.open(fileobj=temp, mode='r') as tar_temp:
|
||||
for fn in tar_temp.getnames():
|
||||
if not re.match(path, fn):
|
||||
continue
|
||||
lst.append(fn)
|
||||
return sorted(lst)
|
||||
|
||||
|
||||
def _extract_from_zip(bytes, fn):
|
||||
with io.BytesIO(bytes) as temp:
|
||||
with zipfile.ZipFile(temp, mode='r') as zip_temp:
|
||||
with zip_temp.open(fn) as member_fh:
|
||||
return member_fh.read()
|
||||
|
||||
|
||||
def _list_from_zip(bytes, path):
|
||||
lst = list()
|
||||
with io.BytesIO(bytes) as temp:
|
||||
with zipfile.ZipFile(temp, mode='r') as zip_temp:
|
||||
for fn in zip_temp.namelist():
|
||||
if not re.match(path, fn):
|
||||
continue
|
||||
lst.append(fn)
|
||||
return sorted(lst)
|
||||
|
||||
|
||||
def assert_has_archive_member(output_bytes, path, verify_assertions_function, children, all="false", n: int = None, delta: int = 0, min: int = None, max: int = None, negate: bool = False):
|
||||
""" Recursively checks the specified children assertions against the text of
|
||||
the first element matching the specified path found within the archive.
|
||||
Currently supported formats: .zip, .tar, .tar.gz."""
|
||||
all = asbool(all)
|
||||
extract_foo = None
|
||||
# from python 3.9 is_tarfile supports file like objects then we do not need
|
||||
# the tempfile detour but can use io.BytesIO(output_bytes)
|
||||
with tempfile.NamedTemporaryFile() as tmp:
|
||||
tmp.write(output_bytes)
|
||||
tmp.flush()
|
||||
if zipfile.is_zipfile(tmp.name):
|
||||
extract_foo = _extract_from_zip
|
||||
list_foo = _list_from_zip
|
||||
elif tarfile.is_tarfile(tmp.name):
|
||||
extract_foo = _extract_from_tar
|
||||
list_foo = _list_from_tar
|
||||
assert extract_foo is not None, f"Expected path '{path}' to be an archive"
|
||||
|
||||
output_temp = io.BytesIO(output_bytes)
|
||||
try: # tar / tar.gz
|
||||
temp = tarfile.open(fileobj=output_temp, mode='r')
|
||||
contents = _extract_from_tar(temp, path)
|
||||
except tarfile.TarError: # zip
|
||||
temp = zipfile.ZipFile(output_temp, mode='r')
|
||||
contents = _extract_from_zip(temp, path)
|
||||
finally:
|
||||
verify_assertions_function(contents.read(), children)
|
||||
contents.close()
|
||||
temp.close()
|
||||
output_temp.close()
|
||||
# get list of matching file names in archive and check against n, delta,
|
||||
# min, max (slightly abusing the output and text as well as the function
|
||||
# parameters)
|
||||
fns = list_foo(output_bytes, path)
|
||||
_assert_presence_number(None, path, n, delta, min, max, negate,
|
||||
lambda o, t: len(fns) > 0,
|
||||
lambda o, t: len(fns),
|
||||
"{expected} path '{text}' in archive",
|
||||
"{expected} {n}+-{delta} matches for path '{text}' in archive",
|
||||
"{expected} that the number of matches for path '{text}' in archive is in [{min}:{max}]")
|
||||
|
||||
# check sub-assertions on members matching path
|
||||
for fn in fns:
|
||||
contents = extract_foo(output_bytes, fn)
|
||||
try:
|
||||
verify_assertions_function(contents, children)
|
||||
except AssertionError as e:
|
||||
raise AssertionError(f"Archive member '{path}': {str(e)}")
|
||||
if not all:
|
||||
break
|
||||
|
||||
@@ -20,9 +20,10 @@ def assert_has_h5_attribute(output_bytes, key, value):
|
||||
output_temp = io.BytesIO(output_bytes)
|
||||
local_attrs = h5py.File(output_temp, 'r').attrs
|
||||
assert key in local_attrs and str(local_attrs[key]) == value, (
|
||||
f"Not a HDF5 file or H5 attributes do not match:\n\t{local_attrs.items()}\n\n\t({key} : {value})")
|
||||
f"Not a HDF5 file or H5 attributes do not match:\n\t{list(local_attrs.items())}\n\n\t({key} : {value})")
|
||||
|
||||
|
||||
# TODO the function actually queries groups. so the function and argument name are misleading
|
||||
def assert_has_h5_keys(output_bytes, keys):
|
||||
""" Asserts the specified HDF5 output has the given keys."""
|
||||
_assert_h5py()
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
def assert_has_size(output_bytes, value, delta=0):
|
||||
"""Asserts the specified output has a size of the specified value"""
|
||||
from ._util import _assert_number
|
||||
|
||||
|
||||
def assert_has_size(output_bytes, value: int = None, delta: int = 0, min: int = None, max: int = None, negate: bool = False):
|
||||
"""
|
||||
Asserts the specified output has a size of the specified value,
|
||||
allowing for absolute (delta) and relative (delta_frac) difference.
|
||||
"""
|
||||
output_size = len(output_bytes)
|
||||
assert abs(output_size - int(value)) <= int(delta), f"Expected file size was {value}, actual file size was {output_size} (difference of {delta} accepted)"
|
||||
_assert_number(output_size, value, delta, min, max, negate,
|
||||
"{expected} file size of {n}+-{delta}",
|
||||
"{expected} file size to be in [{min}:{max}]")
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
import re
|
||||
|
||||
from ._util import _assert_number
|
||||
|
||||
def get_first_line(output):
|
||||
match = re.search("^(.*)$", output, flags=re.MULTILINE)
|
||||
|
||||
def get_first_line(output, comment):
|
||||
"""
|
||||
get the first non-comment and non-empty line
|
||||
"""
|
||||
if comment != "":
|
||||
match = re.search(f"^([^{comment}].*)$", output, flags=re.MULTILINE)
|
||||
else:
|
||||
match = re.search("^(.+)$", output, flags=re.MULTILINE)
|
||||
if match is None:
|
||||
return None
|
||||
return ""
|
||||
else:
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def assert_has_n_columns(output, n, sep='\t'):
|
||||
def assert_has_n_columns(output, n: int = None, delta: int = 0, min: int = None, max: int = None, sep='\t', comment="", negate: bool = False):
|
||||
""" Asserts the tabular output contains n columns. The optional
|
||||
sep argument specifies the column seperator used to determine the
|
||||
number of columns."""
|
||||
n = int(n)
|
||||
first_line = get_first_line(output)
|
||||
assert first_line is not None, "Was expecting output with %d columns, but output was empty." % n
|
||||
assert len(first_line.split(sep)) == n, "Output does not have %d columns." % n
|
||||
number of columns. The optional comment argument specifies
|
||||
comment characters"""
|
||||
first_line = get_first_line(output, comment)
|
||||
n_columns = len(first_line.split(sep))
|
||||
_assert_number(n_columns, n, delta, min, max, negate,
|
||||
"{expected} {n}+-{delta} columns in output",
|
||||
"{expected} the number of columns in output to be in [{min}:{max}]")
|
||||
|
||||
@@ -1,54 +1,73 @@
|
||||
import re
|
||||
|
||||
from ._util import _assert_number, _assert_presence_number
|
||||
|
||||
def assert_has_text(output, text, n=None):
|
||||
|
||||
def assert_has_text(output, text, n: int = None, delta: int = 0, min: int = None, max: int = None, negate: bool = False):
|
||||
""" Asserts specified output contains the substring specified by
|
||||
the argument text. The exact number of occurrences can be
|
||||
optionally specified by the argument n."""
|
||||
optionally specified by the argument n"""
|
||||
assert output is not None, "Checking has_text assertion on empty output (None)"
|
||||
if n is None:
|
||||
assert output.find(text) >= 0, f"Output file did not contain expected text '{text}' (output '{output}')"
|
||||
else:
|
||||
matches = re.findall(re.escape(text), output)
|
||||
assert len(matches) == int(n), f"Expected {n} matches for '{text}' in output file (output '{output}'); found {len(matches)}"
|
||||
_assert_presence_number(output, text, n, delta, min, max, negate,
|
||||
lambda o, t: o.find(t) >= 0,
|
||||
lambda o, t: len(re.findall(re.escape(t), o)),
|
||||
"{expected} text '{text}' in output ('{output}')",
|
||||
"{expected} {n}+-{delta} occurences of '{text}' in output ('{output}')",
|
||||
"{expected} that the number of occurences of '{text}' in output is in [{min}:{max}] ('{output}')")
|
||||
|
||||
|
||||
def assert_not_has_text(output, text):
|
||||
""" Asserts specified output does not contain the substring
|
||||
specified by the argument text."""
|
||||
specified by the argument text"""
|
||||
assert output is not None, "Checking not_has_text assertion on empty output (None)"
|
||||
assert output.find(text) < 0, f"Output file contains unexpected text '{text}'"
|
||||
|
||||
|
||||
def assert_has_line(output, line, n=None):
|
||||
def assert_has_line(output, line, n: int = None, delta: int = 0, min: int = None, max: int = None, negate: bool = False):
|
||||
""" Asserts the specified output contains the line specified by the
|
||||
argument line. The exact number of occurrences can be optionally
|
||||
specified by the argument n."""
|
||||
specified by the argument n"""
|
||||
assert output is not None, "Checking has_line assertion on empty output (None)"
|
||||
if n is None:
|
||||
match = re.search(f"^{re.escape(line)}$", output, flags=re.MULTILINE)
|
||||
assert match is not None, f"No line of output file was '{line}' (output was '{output}') "
|
||||
else:
|
||||
matches = re.findall(f"^{re.escape(line)}$", output, flags=re.MULTILINE)
|
||||
assert len(matches) == int(n), f"Expected {n} lines matching '{line}' in output file (output was '{output}'); found {len(matches)}"
|
||||
_assert_presence_number(output, line, n, delta, min, max, negate,
|
||||
lambda o, l: re.search(f"^{re.escape(l)}$", o, flags=re.MULTILINE) is not None,
|
||||
lambda o, l: len(re.findall(f"^{re.escape(l)}$", o, flags=re.MULTILINE)),
|
||||
"{expected} line '{text}' in output ('{output}')",
|
||||
"{expected} {n}+-{delta} lines '{text}' in output ('{output}')",
|
||||
"{expected} that the number of lines '{text}' in output is in [{min}:{max}] ('{output}')")
|
||||
|
||||
|
||||
def assert_has_n_lines(output, n):
|
||||
"""Asserts the specified output contains ``n`` lines."""
|
||||
def assert_has_n_lines(output, n: int = None, delta: int = 0, min: int = None, max: int = None, negate: bool = False):
|
||||
"""Asserts the specified output contains ``n`` lines allowing
|
||||
for a difference in the number of lines (delta)
|
||||
or relative differebce in the number of lines"""
|
||||
assert output is not None, "Checking has_n_lines assertion on empty output (None)"
|
||||
n_lines_found = len(output.splitlines())
|
||||
assert n_lines_found == int(n), f"Expected {n} lines in output, found {n_lines_found} lines"
|
||||
count = len(output.splitlines())
|
||||
_assert_number(count, n, delta, min, max, negate,
|
||||
"{expected} {n}+-{delta} lines in the output",
|
||||
"{expected} the number of line to be in [{min}:{max}]")
|
||||
|
||||
|
||||
def assert_has_text_matching(output, expression):
|
||||
def assert_has_text_matching(output, expression, n: int = None, delta: int = 0, min: int = None, max: int = None, negate: bool = False):
|
||||
""" Asserts the specified output contains text matching the
|
||||
regular expression specified by the argument expression."""
|
||||
match = re.search(expression, output)
|
||||
assert match is not None, f"No text matching expression '{expression}' was found in output file."
|
||||
regular expression specified by the argument expression.
|
||||
If n is given the assertion checks for exacly n (nonoverlapping)
|
||||
occurences.
|
||||
"""
|
||||
_assert_presence_number(output, expression, n, delta, min, max, negate,
|
||||
lambda o, e: re.search(e, o) is not None,
|
||||
lambda o, e: len(re.findall(e, o)),
|
||||
"{expected} text matching expression '{text}' in output ('{output}')",
|
||||
"{expected} {n}+-{delta} (non-overlapping) matches for '{text}' in output ('{output}')",
|
||||
"{expected} that the number of (non-overlapping) matches for '{text}' in output is in [{min}:{max}] ('{output}')")
|
||||
|
||||
|
||||
def assert_has_line_matching(output, expression):
|
||||
def assert_has_line_matching(output, expression, n: int = None, delta: int = 0, min: int = None, max: int = None, negate: bool = False):
|
||||
""" Asserts the specified output contains a line matching the
|
||||
regular expression specified by the argument expression."""
|
||||
match = re.search(f"^{expression}$", output, flags=re.MULTILINE)
|
||||
assert match is not None, f"No line matching expression '{expression}' was found in output file."
|
||||
regular expression specified by the argument expression. If n is given
|
||||
the assertion checks for exactly n occurences."""
|
||||
_assert_presence_number(output, expression, n, delta, min, max, negate,
|
||||
lambda o, e: re.search(f"^{e}$", o, flags=re.MULTILINE) is not None,
|
||||
lambda o, e: len(re.findall(f"^{e}$", o, flags=re.MULTILINE)),
|
||||
"{expected} line matching expression '{text}' in output ('{output}')",
|
||||
"{expected} {n}+-{delta} lines matching for '{text}' in output ('{output}')",
|
||||
"{expected} that the number of lines matching for '{text}' in output is in [{min}:{max}] ('{output}')")
|
||||
|
||||
@@ -1,91 +1,105 @@
|
||||
import re
|
||||
|
||||
from lxml.etree import XMLSyntaxError
|
||||
|
||||
from galaxy.tool_util.verify import asserts
|
||||
from galaxy.util import (
|
||||
asbool,
|
||||
parse_xml_string,
|
||||
unicodify,
|
||||
)
|
||||
|
||||
|
||||
# Helper functions used to work with XML output.
|
||||
def to_xml(output):
|
||||
return parse_xml_string(output)
|
||||
|
||||
|
||||
def xml_find_text(output, path):
|
||||
xml = to_xml(output)
|
||||
text = xml.findtext(path)
|
||||
return text
|
||||
|
||||
|
||||
def xml_find(output, path):
|
||||
xml = to_xml(output)
|
||||
return xml.find(path)
|
||||
|
||||
|
||||
def assert_is_valid_xml(output):
|
||||
""" Simple assertion that just verifies the specified output
|
||||
is valid XML."""
|
||||
try:
|
||||
to_xml(output)
|
||||
except Exception as e:
|
||||
# TODO: Narrow caught exception to just parsing failure
|
||||
parse_xml_string(output)
|
||||
except XMLSyntaxError as e:
|
||||
raise AssertionError(f"Expected valid XML, but could not parse output. {unicodify(e)}")
|
||||
|
||||
|
||||
def assert_has_element_with_path(output, path):
|
||||
def assert_has_element_with_path(output, path, negate: bool = False):
|
||||
""" Asserts the specified output has at least one XML element with a
|
||||
path matching the specified path argument. Valid paths are the
|
||||
simplified subsets of XPath implemented by lxml.etree;
|
||||
http://effbot.org/zone/element-xpath.htm for more information."""
|
||||
if xml_find(output, path) is None:
|
||||
errmsg = f"Expected to find XML element matching expression {path}, not such match was found."
|
||||
raise AssertionError(errmsg)
|
||||
https://lxml.de/xpathxslt.html for more information."""
|
||||
assert_xml_element(output, path, negate=negate)
|
||||
|
||||
|
||||
def assert_has_n_elements_with_path(output, path, n):
|
||||
def assert_has_n_elements_with_path(output, path, n: int = None, delta: int = 0, min: int = None, max: int = None, negate: bool = False):
|
||||
""" Asserts the specified output has exactly n elements matching the
|
||||
path specified."""
|
||||
xml = to_xml(output)
|
||||
n = int(n)
|
||||
num_elements = len(xml.findall(path))
|
||||
if num_elements != n:
|
||||
errmsg = "Expected to find %d elements with path %s, but %d were found." % (n, path, num_elements)
|
||||
raise AssertionError(errmsg)
|
||||
assert_xml_element(output, path, n=n, delta=delta, min=min, max=max, negate=negate)
|
||||
|
||||
|
||||
def assert_element_text_matches(output, path, expression):
|
||||
def assert_element_text_matches(output, path, expression, negate: bool = False):
|
||||
""" Asserts the text of the first element matching the specified
|
||||
path matches the specified regular expression."""
|
||||
text = xml_find_text(output, path)
|
||||
if re.match(expression, text) is None:
|
||||
errmsg = f"Expected element with path '{path}' to contain text matching '{expression}', instead text '{text}' was found."
|
||||
raise AssertionError(errmsg)
|
||||
sub = {"tag": "has_text_matching", 'attributes': {'expression': expression, 'negate': negate}}
|
||||
assert_xml_element(output, path, asserts.verify_assertions, [sub])
|
||||
|
||||
|
||||
def assert_element_text_is(output, path, text):
|
||||
def assert_element_text_is(output, path, text, negate: bool = False):
|
||||
""" Asserts the text of the first element matching the specified
|
||||
path matches exactly the specified text. """
|
||||
assert_element_text_matches(output, path, re.escape(text))
|
||||
assert_element_text_matches(output, path, re.escape(text) + "$", negate=negate)
|
||||
|
||||
|
||||
def assert_attribute_matches(output, path, attribute, expression):
|
||||
def assert_attribute_matches(output, path, attribute, expression, negate: bool = False):
|
||||
""" Asserts the specified attribute of the first element matching
|
||||
the specified path matches the specified regular expression."""
|
||||
xml = xml_find(output, path)
|
||||
attribute_value = xml.attrib[attribute]
|
||||
if re.match(expression, attribute_value) is None:
|
||||
errmsg = f"Expected attribute '{attribute}' on element with path '{path}' to match '{expression}', instead attribute value was '{attribute_value}'."
|
||||
raise AssertionError(errmsg)
|
||||
sub = {"tag": "has_text_matching", 'attributes': {'expression': expression, 'negate': negate}}
|
||||
assert_xml_element(output, path, asserts.verify_assertions, [sub], attribute=attribute)
|
||||
|
||||
|
||||
def assert_attribute_is(output, path, attribute, text):
|
||||
def assert_attribute_is(output, path, attribute, text, negate: bool = False):
|
||||
""" Asserts the specified attribute of the first element matching
|
||||
the specified path matches exactly the specified text."""
|
||||
assert_attribute_matches(output, path, attribute, re.escape(text))
|
||||
assert_attribute_matches(output, path, attribute, re.escape(text) + "$", negate=negate)
|
||||
|
||||
|
||||
def assert_element_text(output, path, verify_assertions_function, children):
|
||||
def assert_element_text(output, path, verify_assertions_function, children, negate: bool = False):
|
||||
""" Recursively checks the specified assertions against the text of
|
||||
the first element matching the specified path."""
|
||||
text = xml_find_text(output, path)
|
||||
verify_assertions_function(text, children)
|
||||
assert_xml_element(output, path, verify_assertions_function, children, negate=negate)
|
||||
|
||||
|
||||
def assert_xml_element(output, path, verify_assertions_function=None, children=None, attribute=None, all=False, n: int = None, delta: int = 0, min: int = None, max: int = None, negate: bool = False):
|
||||
"""
|
||||
Check if path occurs in the xml. If n and delta or min and max are given
|
||||
also the number of occurences is checked.
|
||||
If there are any sub assertions then check them against
|
||||
- the element's text if attribute is None
|
||||
- the content of the attribute
|
||||
If all is True then the sub assertions are checked for all occurences.
|
||||
"""
|
||||
children = children or []
|
||||
all = asbool(all)
|
||||
# assert that path is in output (the specified number of times)
|
||||
|
||||
xml = parse_xml_string(output)
|
||||
asserts._util._assert_presence_number(xml, path, n, delta, min, max, negate,
|
||||
lambda x, p: x.find(p) is not None,
|
||||
lambda x, p: len(x.findall(p)),
|
||||
"{expected} path '{text}' in xml",
|
||||
"{expected} {n}+-{delta} occurrences of path '{text}' in xml",
|
||||
"{expected} that the number of occurences of path '{text}' in xml is in [{min}:{max}]")
|
||||
|
||||
# check sub-assertions
|
||||
if len(children) == 0 or verify_assertions_function is None:
|
||||
return
|
||||
for occ in xml.findall(path):
|
||||
if attribute is None or attribute == "":
|
||||
content = occ.text
|
||||
else:
|
||||
content = occ.attrib[attribute]
|
||||
try:
|
||||
verify_assertions_function(content, children)
|
||||
except AssertionError as e:
|
||||
if attribute is not None and attribute != "":
|
||||
raise AssertionError(f"Attribute '{attribute}' on element with path '{path}': {str(e)}")
|
||||
else:
|
||||
raise AssertionError(f"Text of element with path '{path}': {str(e)}")
|
||||
if not all:
|
||||
break
|
||||
|
||||
+596
-120
@@ -1778,7 +1778,6 @@ provides a demonstration of using this tag.
|
||||
</output>
|
||||
</test>
|
||||
```
|
||||
|
||||
]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
@@ -2016,128 +2015,561 @@ module.
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:group ref="TestAssertion" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:group ref="TestAssertionsGeneral" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="TestAssertionsText" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="TestAssertionsTabular" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="TestAssertionsArchive" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="TestAssertionsXml" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="TestAssertionsH5" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
<xs:group name="TestAssertion">
|
||||
<xs:group name="TestAssertionsGeneral">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[General asserions]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:choice>
|
||||
<xs:element name="has_text" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts the specified ``text`` appears in the output (e.g. ``<has_text text="chr7">``). If the ``text`` is expected to occur a particular number of times, this value can be specified using ``n``.]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="not_has_text" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts the specified ``text`` does not appear in the output (e.g. ``<not_has_text text="chr8" />``).]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="has_text_matching" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts text matching the specified regular expression (``expression``) appears in the output (e.g. ``<has_text_matching expression="1274\d+53" />`` ).]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="has_line" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts a line matching the specified string (``line``) appears in the output (e.g. ``<has_line line="A full example line." />``). If the ``line`` is expected to occur a particular number of times, this value can be specified using ``n``.]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="has_n_lines" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts that an output contains ``n`` lines, e.g. ``<has_n_lines n="3" />``.]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="has_line_matching" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts a line matching the specified regular expression (``expression``) appears in the output (e.g. ``<has_line_matching expression=".*\s+127489808\s+127494553" />``).]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="has_n_columns" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts tabular output contains the specified number (``n``) of columns (e.g. ``<has_n_columns n="3" />``).]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="is_valid_xml" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts the output is a valid XML file (e.g. ``<is_valid_xml />``).]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="has_element_with_path" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts the XML output contains at least one element (or tag) with the specified XPath-like ``path`` (e.g. ``<has_element_with_path path="BlastOutput_param/Parameters/Parameters_matrix" />``).]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="has_n_elements_with_path" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts the XML output contains the specified number (``n``) of elements (or tags) with the specified XPath-like ``path`` (e.g. ``<has_n_elements_with_path n="9" path="BlastOutput_iterations/Iteration/Iteration_hits/Hit/Hit_num" />``).]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="element_text_is" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts the text of the XML element with the specified XPath-like ``path`` is the specified ``text`` (e.g. ``<element_text_is path="BlastOutput_program" text="blastp" />``).]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="element_text_matches">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts the text of the XML element with the specified XPath-like ``path`` matches the regular expression defined by ``expression`` (e.g. ``<element_text_matches path="BlastOutput_version" expression="BLASTP\s+2\.2.*" />``).]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="attribute_is" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts the XML ``attribute`` for the element (or tag) with the specified XPath-like ``path`` is the specified ``text`` (e.g. ``<attribute_is path="outerElement/innerElement1" attribute="foo" text="bar" />`` ).]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="attribute_matches" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts the XML ``attribute`` for the element (or tag) with the specified XPath-like ``path`` matches the regular expression specified by ``expression`` (e.g. ``<attribute_matches path="outerElement/innerElement2" attribute="foo2" expression="bar\d+" />``).]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="element_text" type="xs:anyType">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[This tag allows the developer to recurisively specify additional assertions as child elements about just the text contained in the element specified by the XPath-like ``path`` (e.g. ``<element_text path="BlastOutput_iterations/Iteration/Iteration_hits/Hit/Hit_def"><not_has_text text="EDK72998.1" /></element_text>``).]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="has_h5_keys" type="AssertHasH5Keys">
|
||||
</xs:element>
|
||||
<xs:element name="has_h5_attribute" type="AssertHasH5Attribute">
|
||||
</xs:element>
|
||||
<xs:element name="has_archive_member" type="AssertHasArchiveMember">
|
||||
</xs:element>
|
||||
<xs:element name="has_size" type="AssertHasSize">
|
||||
</xs:element>
|
||||
<xs:element name="has_size" type="AssertHasSize"/>
|
||||
</xs:choice>
|
||||
</xs:group>
|
||||
<xs:group name="TestAssertionsText">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Assertions for text data]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:choice>
|
||||
<xs:element name="has_text" type="AssertHasText"/>
|
||||
<xs:element name="not_has_text" type="AssertNotHasText"/>
|
||||
<xs:element name="has_text_matching" type="AssertHasTextMatching"/>
|
||||
<xs:element name="has_line" type="AssertHasLine"/>
|
||||
<xs:element name="has_line_matching" type="AssertHasLineMatching"/>
|
||||
<xs:element name="has_n_lines" type="AssertHasNLines"/>
|
||||
</xs:choice>
|
||||
</xs:group>
|
||||
<xs:group name="TestAssertionsTabular">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Assertions for tabular data]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:choice>
|
||||
<xs:element name="has_n_columns" type="AssertHasNColumns"/>
|
||||
</xs:choice>
|
||||
</xs:group>
|
||||
<xs:group name="TestAssertionsArchive">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Assertions for archives (zip/tar)]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:choice>
|
||||
<xs:element name="has_archive_member" type="AssertHasArchiveMember"/>
|
||||
</xs:choice>
|
||||
</xs:group>
|
||||
<xs:group name="TestAssertionsXml">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Assertions for XML data]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:choice>
|
||||
<xs:element name="is_valid_xml" type="AssertIsValidXML"/>
|
||||
<xs:element name="xml_element" type="AssertXMLElement"/>
|
||||
<xs:element name="has_element_with_path" type="AssertHasElementWithPath"/>
|
||||
<xs:element name="has_n_elements_with_path" type="AssertHasNElementsWithPath"/>
|
||||
<xs:element name="element_text_matches" type="AssertElementTextMatches"/>
|
||||
<xs:element name="element_text_is" type="AssertElementTextIs"/>
|
||||
<xs:element name="attribute_matches" type="AssertAttributeMatches"/>
|
||||
<xs:element name="attribute_is" type="AssertAttributeIs"/>
|
||||
<xs:element name="element_text" type="AssertElementText"/>
|
||||
</xs:choice>
|
||||
</xs:group>
|
||||
<xs:group name="TestAssertionsH5">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Assertions for H5 data]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:choice>
|
||||
<xs:element name="has_h5_keys" type="AssertHasH5Keys"/>
|
||||
<xs:element name="has_h5_attribute" type="AssertHasH5Attribute"/>
|
||||
</xs:choice>
|
||||
</xs:group>
|
||||
<xs:complexType name="AssertHasSize">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts the output has specific size (in bytes), e.g. ``<has_size value="10000" delta="100" />``.]]></xs:documentation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts the output has a specific size (in bytes) of ``value`` plus minus
|
||||
``delta``, e.g. ``<has_size value="10000" delta="100" />``.
|
||||
Alternatively the range of the expected size can be specified by ``min`` and/or
|
||||
``max``.
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="value" type="xs:string">
|
||||
<xs:attribute name="value" type="Bytes" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Desired size of the outpyt (in bytes)</xs:documentation>
|
||||
<xs:documentation xml:lang="en">Desired size of the output (in bytes), can be suffixed by ``(k|M|G|T|P|E)i?``</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="delta" type="xs:integer" use="optional">
|
||||
<xs:attribute name="delta" type="Bytes" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Maximum allowed size difference (default is 0).</xs:documentation>
|
||||
<xs:documentation xml:lang="en">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?``</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="min" type="Bytes" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Minimum expected size, can be suffixed by ``(k|M|G|T|P|E)i?``</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="max" type="Bytes" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Maximum expected size, can be suffixed by ``(k|M|G|T|P|E)i?``</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasText">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts the specified ``text`` appears in the output (e.g. ``<has_text
|
||||
text="chr7">``). If the ``text`` is expected to occur a particular number of
|
||||
times, this value can be specified using ``n``. Optionally also with a certain
|
||||
``delta``. Alternatively the range of expected occurences can be specified by
|
||||
``min`` and/or ``max``.
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="text" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Text to check for</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeN"/>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertNotHasText">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts the specified ``text`` does not appear in the output (e.g. ``<not_has_text text="chr8" />``).
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="text" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Text to check for</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasTextMatching">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts text matching the specified regular expression (``expression``) appears
|
||||
in the output (e.g. ``<has_text_matching expression="1274\d+53" />`` ). If the
|
||||
regular expression is expected to match a particular number of times, this value
|
||||
can be specified using ``n``. Note only non-overlapping occurences are counted.
|
||||
Optionally also with a certain ``delta``. Alternatively the range of expected
|
||||
occurences can be specified by ``min`` and/or ``max``.
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="expression" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Regular expression to check for</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeN"/>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasLine">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts a line matching the specified string (``line``) appears in the output
|
||||
(e.g. ``<has_line line="A full example line." />``). If the ``line`` is expected
|
||||
to occur a particular number of times, this value can be specified using ``n``.
|
||||
Optionally also with a certain ``delta``. Alternatively the range of expected
|
||||
occurences can be specified by ``min`` and/or ``max``.
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="line" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The line to check for</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeN"/>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasNLines">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts that an output contains ``n`` lines, allowing for a difference of
|
||||
``delta`` (default is 0), e.g. ``<has_n_lines n="3" delta="1"/>``.
|
||||
Alternatively the range of expected occurences can be specified by ``min``
|
||||
and/or ``max``.
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attributeGroup ref="AssertAttributeN"/>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasLineMatching">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts a line matching the specified regular expression (``expression``)
|
||||
appears in the output (e.g. ``<has_line_matching expression=".*\s+127489808\s+127494553" />``).
|
||||
If a particular number of matching lines is expected, this value can be
|
||||
specified using ``n``. Optionally also with ``delta``. Alternatively the range
|
||||
of expected occurences can be specified by ``min`` and/or ``max``.
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="expression" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Regular expression to check for</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeN"/>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasNColumns">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts tabular output (actually only the first line) contains the specified
|
||||
number (``n``) of columns (e.g. ``<has_n_columns n="3"/>``) optionally also 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) can be specified (``comment``, default is
|
||||
empty string), then the first non-comment line is used for determining the
|
||||
number of columns.
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attributeGroup ref="AssertAttributeN"/>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasArchiveMember">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
This tag allows to check if ``path`` is contained in a compressed file. The path
|
||||
is a regular expression that is matched against the full paths of the objects in
|
||||
the compressed file (remember that "matching" means it is checked if a prefix of
|
||||
the full path of an archive member is described by the regular expression).
|
||||
Valid archive formats include ``.zip``, ``.tar``, and ``.tar.gz``. Note that
|
||||
depending on the archive creation method:
|
||||
|
||||
- full paths of the members may be prefixed with ``./``
|
||||
- directories may be treated as empty files
|
||||
|
||||
```xml
|
||||
<has_archive_member path="./path/to/my-file.txt"/>
|
||||
```
|
||||
|
||||
With ``n`` and ``delta`` (or ``min`` and ``max``) assertions on the number of
|
||||
archive members matching ``path`` can be expressed. The following could be used,
|
||||
e.g., to assert an archive containing n±1 elements out of which at least
|
||||
4 need to have a ``txt`` extension.
|
||||
|
||||
```xml
|
||||
<has_archive_member path=".*" n="10" delta="1"/>
|
||||
<has_archive_member path=".*\.txt" min="4"/>
|
||||
```
|
||||
|
||||
In addition the tag can contain additional assertions as child elements about
|
||||
the first member in the archive matching the regular expression ``path``. For
|
||||
instance
|
||||
|
||||
```xml
|
||||
<has_archive_member path=".*/my-file.txt">
|
||||
<not_has_text text="EDK72998.1"/>
|
||||
</has_archive_member>
|
||||
```
|
||||
|
||||
If the ``all`` attribute is set to ``true`` then all archive members are subject
|
||||
to the assertions. Note that, archive members matching the ``path`` are sorted
|
||||
alphabetically.
|
||||
|
||||
The ``negate`` attribute of the ``has_archive_member`` assertion only affects
|
||||
the asserts on the presence and number of matching archive members, but not any
|
||||
sub-assertions (which can offer the ``negate`` attribute on their own). The
|
||||
check if the file is an archive at all, which is also done by the function, is
|
||||
not affected.
|
||||
|
||||
$attribute_list::5
|
||||
]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:group ref="TestAssertionsGeneral" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="TestAssertionsText" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="TestAssertionsTabular" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="TestAssertionsXml" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:group ref="TestAssertionsH5" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="path" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The regular expression specifying the archive member.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="all" type="PermissiveBoolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Check the sub-assertions for all paths matching the path. Default: false, i.e. only the first </xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeN"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertIsValidXML">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts the output is a valid XML file (e.g. ``<is_valid_xml />``).
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertXMLElement">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><. If ``n`` and ``delta``
|
||||
or ``min`` and ``max`` are given also the number of occurences is checked.
|
||||
|
||||
```xml
|
||||
<assert_contents>
|
||||
<xml_element path="./elem"/>
|
||||
<xml_element path="./elem/more[2]"/>
|
||||
<xml_element path=".//more" n="3" delta="1"/>
|
||||
</assert_contents>
|
||||
```
|
||||
|
||||
With ``negate="true"`` the outcome of the assertions wrt the precence and number
|
||||
of ``path`` can be negated. If there are any sub assertions then check them against
|
||||
|
||||
- the content of the attribute ``attribute``
|
||||
- the element's text if no attribute is given
|
||||
|
||||
```xml
|
||||
<assert_contents>
|
||||
<xml_element path="./elem/more[2]" attribute="name">
|
||||
<has_text_matching expression="foo$"/>
|
||||
</xml_element>
|
||||
</assert_contents>
|
||||
```
|
||||
|
||||
Sub-assertions are not subject to the ``negate`` attribute of ``xml_element``.
|
||||
If ``all`` is ``true`` then the sub assertions are checked for all occurences.
|
||||
|
||||
Note that all other XML assertions can be expressed by this assertion (Galaxy
|
||||
also implements the other assertions by calling this one).
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attributeGroup ref="AssertAttributePath"/>
|
||||
<xs:attribute name="all" type="PermissiveBoolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Check the sub-assertions for all paths matching the path. Default: false, i.e. only the first</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="attribute" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The name of the attribute to apply sub-assertion on. If not given then the element text is used</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeN"/>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasElementWithPath">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts the XML output contains at least one element (or tag) with the specified
|
||||
XPath-like ``path``, e.g.
|
||||
|
||||
```xml
|
||||
<has_element_with_path path="BlastOutput_param/Parameters/Parameters_matrix" />
|
||||
```
|
||||
|
||||
With ``negate`` the result of the assertion can be inverted.
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attributeGroup ref="AssertAttributePath"/>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasNElementsWithPath">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts the XML output contains the specified number (``n``, optionally with ``delta``) of elements (or
|
||||
tags) with the specified XPath-like ``path``, e.g.
|
||||
|
||||
```xml
|
||||
<has_n_elements_with_path n="9" path="BlastOutput_iterations/Iteration/Iteration_hits/Hit/Hit_num" />
|
||||
```
|
||||
|
||||
Alternatively to ``n`` and ``delta`` also the ``min`` and ``max`` attributes
|
||||
can be used to specify the range of the expected number of occurences.
|
||||
With ``negate`` the result of the assertion can be inverted.
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attributeGroup ref="AssertAttributePath"/>
|
||||
<xs:attributeGroup ref="AssertAttributeN"/>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertElementTextMatches">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts the text of the XML element with the specified XPath-like ``path``
|
||||
matches the regular expression defined by ``expression``, e.g.
|
||||
|
||||
```xml
|
||||
<element_text_matches path="BlastOutput_version" expression="BLASTP\s+2\.2.*"/>
|
||||
```
|
||||
|
||||
The assertion implicitly also asserts that an element matching ``path`` exists.
|
||||
With ``negate`` the result of the assertion (on the matching) can be inverted (the
|
||||
implicit assertion on the existence of the path is not affected).
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attributeGroup ref="AssertAttributePath"/>
|
||||
<xs:attribute name="expression" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The regular expression to use.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertElementTextIs">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts the text of the XML element with the specified XPath-like ``path`` is
|
||||
the specified ``text``, e.g.
|
||||
|
||||
```xml
|
||||
<element_text_is path="BlastOutput_program" text="blastp" />
|
||||
```
|
||||
|
||||
The assertion implicitly also asserts that an element matching ``path`` exists.
|
||||
With ``negate`` the result of the assertion (on the equality) can be inverted (the
|
||||
implicit assertion on the existence of the path is not affected).
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attributeGroup ref="AssertAttributePath"/>
|
||||
<xs:attribute name="text" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Text to check for.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertAttributeMatches">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts the XML ``attribute`` for the element (or tag) with the specified
|
||||
XPath-like ``path`` matches the regular expression specified by ``expression``, e.g.
|
||||
|
||||
```xml
|
||||
<attribute_matches path="outerElement/innerElement2" attribute="foo2" expression="bar\d+" />
|
||||
```
|
||||
|
||||
The assertion implicitly also asserts that an element matching ``path`` exists.
|
||||
With ``negate`` the result of the assertion (on the matching) can be inverted (the
|
||||
implicit assertion on the existence of the path is not affected).
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attributeGroup ref="AssertAttributePath"/>
|
||||
<xs:attribute name="expression" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The regular expression to use.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertAttributeIs">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts the XML ``attribute`` for the element (or tag) with the specified
|
||||
XPath-like ``path`` is the specified ``text``, e.g.
|
||||
|
||||
```xml
|
||||
<attribute_is path="outerElement/innerElement1" attribute="foo" text="bar" />
|
||||
```
|
||||
|
||||
The assertion implicitly also asserts that an element matching ``path`` exists.
|
||||
With ``negate`` the result of the assertion (on the equality) can be inverted (the
|
||||
implicit assertion on the existence of the path is not affected).
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attributeGroup ref="AssertAttributePath"/>
|
||||
<xs:attribute name="text" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Text to check for.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertElementText">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
This tag allows the developer to recurisively specify additional assertions as
|
||||
child elements about just the text contained in the element specified by the
|
||||
XPath-like ``path``, e.g.
|
||||
|
||||
```xml
|
||||
<element_text path="BlastOutput_iterations/Iteration/Iteration_hits/Hit/Hit_def">
|
||||
<not_has_text text="EDK72998.1" />
|
||||
</element_text>
|
||||
```
|
||||
|
||||
The assertion implicitly also asserts that an element matching ``path`` exists.
|
||||
With ``negate`` the result of the implicit assertions can be inverted.
|
||||
The sub-assertions, which have their own ``negate`` attribute, are not affected
|
||||
by ``negate``.
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:group ref="TestAssertionsText" minOccurs="1" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:attributeGroup ref="AssertAttributePath"/>
|
||||
<xs:attributeGroup ref="AssertAttributeNegate"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasH5Keys">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts HDF5 output has a set of attributes (``keys``), specified as a comma-separated list (e.g. ``<has_h5_keys keys="bins,chroms,indexes,pixels,chroms/lengths" />``).]]></xs:documentation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts HDF5 output has a set of attributes (``keys``), specified as a
|
||||
comma-separated list, e.g.
|
||||
|
||||
```xml
|
||||
<has_h5_keys keys="bins,chroms,indexes,pixels,chroms/lengths" />
|
||||
```
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="keys" type="xs:string">
|
||||
<xs:annotation>
|
||||
@@ -2147,7 +2579,16 @@ module.
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasH5Attribute">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Asserts HDF5 output contains the specified ``value`` for an attribute (``key``) (e.g. ``<has_attr key="nchroms" value="15" />``).]]></xs:documentation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
Asserts HDF5 output contains the specified ``value`` for an attribute (``key``), e.g.
|
||||
|
||||
```xml
|
||||
<has_h5_attribute key="nchroms" value="15" />
|
||||
```
|
||||
|
||||
$attribute_list::5
|
||||
]]>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:attribute name="key" type="xs:string">
|
||||
<xs:annotation>
|
||||
@@ -2160,19 +2601,46 @@ module.
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="AssertHasArchiveMember">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[This tag allows the developer to recursively specify additional assertions as child elements about just the member contained in the archive specified by the regular expression ``path`` (e.g. ``<has_archive_member path=".*/my-file.txt"><not_has_text text="EDK72998.1" /></has_archive_member>``). Valid archive formats include ``.zip``, ``.tar``, and ``.tar.gz``.]]></xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:sequence>
|
||||
<xs:group ref="TestAssertion" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="path" type="xs:string">
|
||||
|
||||
<xs:attributeGroup name="AssertAttributePath">
|
||||
<xs:attribute name="path" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">The regular expression specifying the archive member.</xs:documentation>
|
||||
<xs:documentation xml:lang="en">Path to check for. Valid paths are the simplified subsets of XPath implemented by lxml.etree; https://lxml.de/xpathxslt.html for more information.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:attributeGroup name="AssertAttributeNegate">
|
||||
<xs:attribute name="negate" type="PermissiveBoolean" default="false">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Negate the outcome of the assertion.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:attributeGroup name="AssertAttributeN">
|
||||
<xs:attribute name="n" type="Bytes" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="delta" type="Bytes" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="min" type="Bytes" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="max" type="Bytes" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:complexType name="Inputs">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[Consists of all elements that define the
|
||||
@@ -6287,8 +6755,8 @@ and ``bibtex`` are the only supported options.</xs:documentation>
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Type of comparison to use when comparing
|
||||
test generated output files to expected output files. Currently valid value are
|
||||
``diff`` (the default), ``re_match``, ``sim_size``, ``re_match_multiline``,
|
||||
and ``contains``.</xs:documentation>
|
||||
``diff`` (the default), ``re_match``, ``re_match_multiline``,
|
||||
and ``contains``. In addtion there is ``sim_size`` which is discouraged in fafour of a ``has_size`` assertion.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="diff"/>
|
||||
@@ -6313,6 +6781,14 @@ and ``contains``.</xs:documentation>
|
||||
<xs:enumeration value="no"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="Bytes">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en">Number of bytes alowing for suffix (k|K|M|G|P|E)i? </xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[1-9][0-9]*([kKMGTPE]i?)?"></xs:pattern>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="EdamTopics">
|
||||
<xs:annotation>
|
||||
<xs:documentation xml:lang="en"><![CDATA[
|
||||
|
||||
@@ -549,6 +549,44 @@ TESTS_EXPECT_FAILURE_OUTPUT = """
|
||||
</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>
|
||||
"""
|
||||
|
||||
# tool xml for xml_order linter
|
||||
XML_ORDER = """
|
||||
<tool>
|
||||
@@ -873,6 +911,20 @@ TESTS = [
|
||||
'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:
|
||||
@@ -954,7 +1006,7 @@ TESTS = [
|
||||
'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 = [
|
||||
@@ -997,6 +1049,7 @@ TEST_IDS = [
|
||||
'outputs: format="input"',
|
||||
'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',
|
||||
@@ -1007,7 +1060,7 @@ TEST_IDS = [
|
||||
'tests: without expectations',
|
||||
'tests: param and output names',
|
||||
'tests: expecting failure with outputs',
|
||||
'xml_order'
|
||||
'xml_order',
|
||||
]
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user