import os
import re
from shutil import rmtree
from tempfile import mkdtemp
from galaxy.tool_util.loader import (
load_tool,
template_macro_params,
)
from galaxy.tool_util.unittest_utils.sample_data import (
SIMPLE_MACRO,
SIMPLE_TOOL_WITH_MACRO,
)
from galaxy.util import (
parse_xml,
xml_to_string,
)
class TestToolDirectory:
__test__ = False # Prevent pytest from discovering this class (issue #12071)
def __init__(self):
self.temp_directory = mkdtemp()
def __enter__(self):
return self
def __exit__(self, type, value, tb):
rmtree(self.temp_directory)
def write(self, contents, name="tool.xml"):
open(os.path.join(self.temp_directory, name), "w").write(contents)
def load(self, name="tool.xml", preprocess=True):
path = os.path.join(self.temp_directory, name)
if preprocess:
return load_tool(path)
else:
return parse_xml(path)
def test_no_macros():
"""
Test tool loaded in absence of a macros node.
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("")
tool_dir.load(preprocess=True)
def test_loader_simple():
"""
Test simple macro replacement.
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load(preprocess=False)
assert xml.find("inputs") is None
xml = tool_dir.load(preprocess=True)
assert xml.find("inputs") is not None
def test_loader_external():
"""
Test importing macros from external files
"""
with TestToolDirectory() as tool_dir:
tool_dir.write(SIMPLE_TOOL_WITH_MACRO)
tool_dir.write(SIMPLE_MACRO.substitute(tool_version="2.0"), name="external.xml")
xml = tool_dir.load(preprocess=False)
assert xml.find("inputs") is None
xml = tool_dir.load(preprocess=True)
assert xml.find("inputs") is not None
def test_loader_unnamed_yield():
# Test macros with unnamed yield statements.
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
assert xml.find("/inputs[1]/input").get("name") == "first_input"
assert xml.find("/inputs[2]/input").get("name") == "second_input"
assert xml.find("/inputs[3]/input").get("name") == "third_input"
def test_loader_unnamed_yield_nested():
"""
Test nested macro with yield statements
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
# assert the both yields in the inner macro (paired_options) are expanded
assert xml.find('/inputs/conditional[@name="library"]/when[@value="paired"]/param[@name="test"]') is not None
assert (
xml.find('/inputs/conditional[@name="library"]/when[@value="paired_collection"]/param[@name="test"]')
is not None
)
def test_loader_recursive():
"""
Test recursive macro applications.
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
assert xml.find("/inputs/input[1]").get("name") == "first_input"
assert xml.find("/inputs/input[2]").get("name") == "second_input"
assert xml.find("/inputs/input[3]").get("name") == "third_input"
def test_loader_recursive2():
"""
Test recursive macro applications.
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
assert xml.find("/inputs/input[1]").get("name") == "first_input"
assert xml.find("/inputs/input[2]").get("name") == "second_input"
assert xml.find("/inputs/input[3]").get("name") == "third_input"
def test_loader_toplevel_yield():
"""
test expansion of top level (ie child of ) yields
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
assert xml.find("/inputs/param[1]").get("name") == "a1"
assert xml.find("/inputs/param[2]").get("name") == "b"
def test_loader_shortcut():
"""
Test is shortcut for macro type="xml"
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
assert xml.find("inputs") is not None
def test_loader_template():
with TestToolDirectory() as tool_dir:
tool_dir.write("""
tool_wrapper.py
#include source=$tool_params
-a 1 -b 2
""")
xml = tool_dir.load()
params_dict = template_macro_params(xml.getroot())
assert params_dict["tool_params"] == "-a 1 -b 2"
def test_loader_token_text():
with TestToolDirectory() as tool_dir:
tool_dir.write("""
The citation.@CITATION@
""")
xml = tool_dir.load()
help_el = xml.find("help")
assert help_el.text == "The citation.", help_el.text
def test_loader_token_nested():
with TestToolDirectory() as tool_dir:
tool_dir.write("""
Ab@HELPER@rara@WITCH@abcad@WIZARD@
""")
xml = tool_dir.load()
help_el = xml.find("help")
assert help_el.text == "Abracadabra", help_el.text
def test_loader_token_cycle():
"""
test if cycles in nested tokens are detected
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
a @ROSE@rose @IS@a @A@@A@
""")
try:
tool_dir.load()
except Exception as e:
assert str(e) == "Token '@IS@' cannot contain itself"
else:
raise AssertionError("Cycle not detected, but then we are doomed anyway")
def test_loader_token_attribute_name():
"""
test the repacement of an attribute name by a token
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
name
""")
xml = tool_dir.load()
assert xml.find('/another/tag[@name="blah"]') is not None
def test_loader_token_attribute_value():
"""
test the repacement of an attribute value by a token
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
The value.
""")
xml = tool_dir.load()
assert xml.find('/another/tag[@value="The value."]') is not None
def test_loader_token_empty():
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
assert xml.find('/another/tag[@value=""]') is not None
def test_loader_macro_token_quote():
"""
Test macros XML macros with $$ expansions in attributes
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
input_els = xml.findall("inputs")
assert len(input_els) == 3
assert input_els[0].attrib["type"] == "the type is hello"
assert input_els[1].attrib["type"] == "the type is my awesome"
assert input_els[2].attrib["type"] == "the type is doggo"
def test_loader_macro_token():
"""
Test macros XML macros with @ expansions in text
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
@FOO@
""")
xml = tool_dir.load()
input_els = xml.findall("inputs")
assert len(input_els) == 3
assert input_els[0].text == "hello"
assert input_els[1].text == "world"
assert input_els[2].text == "the_default"
def test_loader_macro_token_recursive():
"""
Test macros XML macros with @ expansions and recursive
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
@FOO@
""")
xml = tool_dir.load()
input_els = xml.findall("inputs")
assert len(input_els) == 3
assert input_els[0].find("cow").text == "hello"
assert input_els[1].find("cow").text == "world"
assert input_els[2].find("cow").text == "the_default"
def test_loader_macro_named_yield():
"""
test expansion of named and unnamed yield
- named yields are replaced by content of the corresponding token
- unnamed yields are replaced by all non-token elements of the expand tag
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
assert xml_to_string(xml, pretty=True) == """
"""
def test_loader_macro_multiple_toplevel_yield():
"""
test replacement of multiple top level yield
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
assert xml_to_string(xml, pretty=True) == """
"""
def test_loader_macro_recursive_named_yield():
"""
test 'recursive' replacement with named yields
since named yields are processed in order of the definition of the
corresponding tokens:
- replacing yield for token1 introduces yield for token2
- replacing yield for token2 introduced unnamed yield
- replacing unnamed yield gives the only non-token element of the expand
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
xml = tool_dir.load()
assert xml_to_string(xml, pretty=True) == """
"""
def test_loader_specify_nested_macro_by_token():
"""
test if a nested macro can have a nested
macro specifying the macro name via a token
of the outer macro
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
external.xml""")
tool_dir.write(
"""
""",
name="external.xml",
)
xml = tool_dir.load()
# test with re because loading from external macros
# adds a xml:base property (containing the source path)
# to the node which is printed
print(f"{xml_to_string(xml, pretty=True)}")
assert re.match(
r"""<\?xml version="1\.0" \?>
""",
xml_to_string(xml, pretty=True),
re.MULTILINE,
)
def test_loader_circular_macros():
"""
check the cycles in nested macros are detected
"""
with TestToolDirectory() as tool_dir:
tool_dir.write("""
""")
try:
tool_dir.load()
except AssertionError as a:
assert str(a) == "Cycle in nested macros: already expanded ['a', 'b'] can't expand 'a' again"