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 """) 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@ra ra@WITCH@ab cad @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"