def test_find_tests_section_without_section_returns_none(self) -> None:
        document = TestUtils.to_document("<tool></tool>")
        tool = GalaxyToolXmlDocument(document)

        actual = tool.find_element("notexistent")

        assert actual is None
    def test_get_element_content_range_of_element_returns_expected(self, source: str, element: str, expected: Range) -> None:
        document = TestUtils.to_document(source)
        tool = GalaxyToolXmlDocument(document)
        node = tool.find_element(element)

        actual = tool.get_element_content_range(node)

        assert actual == expected
    def test_get_element_content_range_of_unknown_element_returns_none(self) -> None:
        document = TestUtils.to_document("<tool><tests></tests></tool>")
        tool = GalaxyToolXmlDocument(document)
        node = tool.find_element("unknown")

        actual = tool.get_element_content_range(node)

        assert actual is None
    def test_find_tests_section_returns_expected(self) -> None:
        document = TestUtils.to_document("<tool><tests></tests></tool>")
        tool = GalaxyToolXmlDocument(document)

        actual = tool.find_element("tests")

        assert actual
        assert actual.name == "tests"
    def test_get_position_after_element_returns_expected_position(
        self, source: str, element_name: str, expected_position: Position
    ) -> None:
        document = TestUtils.to_document(source)
        tool = GalaxyToolXmlDocument(document)
        element = tool.find_element(element_name, maxlevel=4)

        assert element is not None
        actual_position = tool.get_position_after(element)
        assert actual_position == expected_position
 def get_macro_names(self, tool_xml: XmlDocument) -> Set[str]:
     tool = GalaxyToolXmlDocument.from_xml_document(tool_xml)
     macros = self._get_macro_definitions(tool_xml)
     imported_macro_files = self._get_imported_macro_files_from_tool(tool)
     for file in imported_macro_files.values():
         macros.update(file.macros)
     return set(macros.keys())
    def validate_document(self, xml_document: XmlDocument) -> List[Diagnostic]:
        """Validates the XML document and returns a list of diagnostics
        if there are any problems.

        Args:
            xml_document (XmlDocument): The XML document. Can be a tool wrapper or macro
            definition file.

        Returns:
            List[Diagnostic]: The list of issues found in the document.
        """

        syntax_errors = self._check_syntax(xml_document.document)
        if syntax_errors:
            return syntax_errors

        if not xml_document.is_tool_file:
            return []
        tool = GalaxyToolXmlDocument.from_xml_document(xml_document)
        try:
            xml_tree = etree.fromstring(tool.source)
            return self._validate_tree(xml_tree)
        except ExpandMacrosFoundException:
            result = self._validate_expanded(tool)
            return result
        except etree.XMLSyntaxError as e:
            return self._build_diagnostics_from_syntax_error(e)
 def _build_diagnostics_for_expanded_macros(
         self, tool: GalaxyToolXmlDocument,
         invalid_document_error) -> List[Diagnostic]:
     virtual_uri = tool.xml_document.document.uri.replace(
         "file", "gls-expand")
     diagnostics = [
         Diagnostic(
             range=tool.get_macros_range(),
             message=error.message,
             source=self.server_name,
             code=DiagnosticCodes.INVALID_EXPANDED_TOOL,
             related_information=[
                 DiagnosticRelatedInformation(
                     message=
                     ("The validation error ocurred on the expanded version of "
                      "the document, i.e. after replacing macros. "
                      "Click here to preview the expanded document."),
                     location=Location(
                         uri=f"{virtual_uri}{EXPAND_DOCUMENT_URI_SUFFIX}",
                         range=Range(
                             start=Position(line=error.line - 1,
                                            character=error.column),
                             end=Position(line=error.line - 1,
                                          character=error.column),
                         ),
                     ),
                 )
             ],
         )
         for error in invalid_document_error.error_log.filter_from_errors()
     ]
     return diagnostics
 def generate_command(
         self, document: Document) -> Optional[GeneratedSnippetResult]:
     """Generates a boilerplate Cheetah code snippet based on the current inputs and outputs
     of this tool wrapper."""
     tool = GalaxyToolXmlDocument(document)
     generator = GalaxyToolCommandSnippetGenerator(tool)
     return generator.generate_snippet()
 def generate_tests(self,
                    document: Document) -> Optional[GeneratedSnippetResult]:
     """Generates a code snippet with some tests for the current inputs and outputs
     of this tool wrapper."""
     tool = GalaxyToolXmlDocument(document)
     generator = GalaxyToolTestSnippetGenerator(tool)
     return generator.generate_snippet()
예제 #11
0
 def _get_test_suite_from_document(
         self, xml_document: XmlDocument) -> Optional[TestSuiteInfoResult]:
     tool = GalaxyToolXmlDocument.from_xml_document(xml_document)
     tool_id = tool.get_tool_id()
     tests_range = tool.get_tests_range()
     if tool_id and tests_range:
         tests = tool.get_tests()
         test_cases: List[TestInfoResult] = []
         id = 1
         for test in tests:
             range = xml_document.get_full_range(test)
             if range:
                 test_cases.append(
                     TestInfoResult(
                         tool_id=tool_id,
                         test_id=str(id),
                         uri=xml_document.document.uri,
                         range=range,
                     ), )
                 id += 1
         return TestSuiteInfoResult(
             tool_id=tool_id,
             uri=xml_document.document.uri,
             range=tests_range,
             children=test_cases,
         )
     return None
예제 #12
0
    def test_generate_snippet_with_tests_section_returns_snippet_only(self) -> None:
        document = TestUtils.to_document("<tool><tests></tests></tool>")
        tool = GalaxyToolXmlDocument(document)
        generator = GalaxyToolTestSnippetGenerator(tool)

        result = generator.generate_snippet()

        assert "<tests>" not in result.snippet
    def test_find_snippet_position_returns_expected_result(self, source: str, expected_position: Position) -> None:
        document = TestUtils.to_document(source)
        tool = GalaxyToolXmlDocument(document)
        generator = GalaxyToolCommandSnippetGenerator(tool)

        actual_position = generator._find_snippet_insert_position()

        assert actual_position == expected_position
예제 #14
0
    def test_generate_snippet_with_command_with_cdata_returns_snippet_only(self) -> None:
        document = TestUtils.to_document("<tool><command><![CDATA[]]></command></tool>")
        tool = GalaxyToolXmlDocument(document)
        generator = GalaxyToolCommandSnippetGenerator(tool)

        result = generator.generate_snippet()

        assert "<command" not in result.snippet
        assert "<![CDATA[" not in result.snippet
    def test_build_snippet_returns_expected_result(self, tool_file: str, expected_snippet_file: str) -> None:
        document = TestUtils.get_test_document_from_file(tool_file)
        expected_snippet = TestUtils.get_test_file_contents(expected_snippet_file)
        tool = GalaxyToolXmlDocument(document)
        generator = GalaxyToolCommandSnippetGenerator(tool)

        actual_snippet = generator._build_snippet()

        assert actual_snippet == expected_snippet
예제 #16
0
 def _edit_create_import_macros_section(self, tool: GalaxyToolXmlDocument,
                                        macros_file_name: str) -> TextEdit:
     """Returns the TextEdit operation that will add a macros file <import> definition to the existing
     <macros> section of a tool wrapper or also create the <macros> section if it doesn't exists."""
     macros_element = tool.find_element(MACROS)
     if macros_element:
         insert_position = tool.get_position_before_first_child(
             macros_element)
         macro_xml = f"<import>{macros_file_name}</import>"
     else:
         insert_position = self._find_macros_insert_position(tool)
         macro_xml = f"<macros>\n<import>{macros_file_name}</import>\n</macros>"
     insert_range = Range(start=insert_position, end=insert_position)
     final_macro_xml = self._adapt_format(tool.xml_document, insert_range,
                                          macro_xml)
     return TextEdit(
         range=insert_range,
         new_text=final_macro_xml,
     )
예제 #17
0
 def _edit_add_macro_to_macros_section(self, tool: GalaxyToolXmlDocument,
                                       macros_element: XmlElement,
                                       macro: MacroData) -> TextEdit:
     """Returns the TextEdit operation that will add a macro definition to the <macros> section of a tool wrapper."""
     insert_position = tool.get_position_after_last_child(macros_element)
     insert_range = Range(start=insert_position, end=insert_position)
     macro_xml = f'<xml name="{macro.name}">\n{macro.content}\n</xml>'
     final_macro_xml = self._adapt_format(tool.xml_document, insert_range,
                                          macro_xml)
     return TextEdit(
         range=insert_range,
         new_text=final_macro_xml,
     )
 def _get_expanded_tool_document(self, tool_document: GalaxyToolXmlDocument) -> GalaxyToolXmlDocument:
     """If the given tool document uses macros, a new tool document with the expanded macros is returned,
     otherwise, the same document is returned.
     """
     if tool_document.uses_macros:
         try:
             document = tool_document.document
             expanded_tool_tree, _ = xml_macros.load_with_references(document.path)
             expanded_tool_tree = cast(etree._ElementTree, expanded_tool_tree)
             expanded_source = etree.tostring(expanded_tool_tree, encoding=str)
             expanded_document = Document(uri=document.uri, source=expanded_source, version=document.version)
             return GalaxyToolXmlDocument(expanded_document)
         except BaseException:
             return tool_document
     return tool_document
예제 #19
0
 def load_macro_definitions(self,
                            tool_xml: XmlDocument) -> ToolMacroDefinitions:
     tool = GalaxyToolXmlDocument.from_xml_document(tool_xml)
     tokens = self._get_token_definitions(tool_xml)
     macros = self._get_macro_definitions(tool_xml)
     imported_macro_files = self._get_imported_macro_files_from_tool(tool)
     for file in imported_macro_files.values():
         tokens.update(file.tokens)
         macros.update(file.macros)
     return ToolMacroDefinitions(
         tool_document=tool_xml,
         imported_macros=imported_macro_files,
         tokens=tokens,
         macros=macros,
     )
예제 #20
0
 def _get_imported_macro_files_from_tool(
         self,
         tool: GalaxyToolXmlDocument) -> Dict[str, ImportedMacrosFile]:
     macro_files = {}
     uris_dict = tool.get_macro_import_uris()
     for file_name, file_uri in uris_dict.items():
         macros_document = self._load_macros_document(file_uri)
         tokens = self._get_token_definitions(macros_document)
         macros = self._get_macro_definitions(macros_document)
         macro_files[file_name] = ImportedMacrosFile(
             file_name=file_name,
             file_uri=file_uri,
             document=macros_document,
             tokens=tokens,
             macros=macros,
         )
     return macro_files
예제 #21
0
 def _calculate_local_changes_for_macro(
         self, tool: GalaxyToolXmlDocument, macro: MacroData,
         params: CodeActionParams) -> Dict[str, List[TextEdit]]:
     """Returns a dictionary with the file uri and the TextEdit operations that will add a macro definition to
     the <macros> section of a tool wrapper. If the <macros> section doesn't exists it will also be created."""
     macros_element = tool.get_macros_element()
     edits: List[TextEdit] = []
     if macros_element is None:
         edits.append(self._edit_create_with_macros_section(tool, macro))
     else:
         edits.append(
             self._edit_add_macro_to_macros_section(tool, macros_element,
                                                    macro))
     edits.append(
         self._edit_replace_range_with_macro_expand(tool, macro,
                                                    params.range))
     changes = {params.text_document.uri: edits}
     return changes
예제 #22
0
 def get_available_refactoring_actions(
         self, xml_document: XmlDocument,
         params: CodeActionParams) -> List[CodeAction]:
     """Gets a collection of possible refactoring code actions on a selected chunk of the document."""
     code_actions = []
     text_in_range = xml_document.get_text_in_range(params.range)
     target_element_tag = self._get_valid_full_element_tag(text_in_range)
     if target_element_tag is not None:
         macro = MacroData(name=target_element_tag,
                           content=text_in_range.strip())
         macro_definitions = self.macros.definitions_provider.load_macro_definitions(
             xml_document)
         tool = GalaxyToolXmlDocument.from_xml_document(xml_document)
         code_actions.extend(
             self.macros.create_extract_to_macros_file_actions(
                 tool, macro_definitions, macro, params))
         code_actions.extend(
             self.macros.create_extract_to_local_macro_actions(
                 tool, macro, params))
     return code_actions
 def _build_diagnostics_for_macros_file_syntax_error(
         self, tool: GalaxyToolXmlDocument,
         syntax_error) -> List[Diagnostic]:
     result = Diagnostic(
         range=tool.get_import_macro_file_range(syntax_error.filename),
         message=syntax_error.msg,
         source=self.server_name,
         related_information=[
             DiagnosticRelatedInformation(
                 message="Syntax error found on imported file.",
                 location=Location(
                     uri=Path(syntax_error.filename).as_uri(),
                     range=Range(
                         start=Position(line=syntax_error.lineno - 1,
                                        character=syntax_error.offset),
                         end=Position(line=syntax_error.lineno - 1,
                                      character=syntax_error.offset),
                     ),
                 ),
             )
         ],
     )
     return [result]
예제 #24
0
    def _find_macros_insert_position(self,
                                     tool: GalaxyToolXmlDocument) -> Position:
        """Returns the position inside the document where the macros section
        can be inserted.

        Returns:
            Range: The position where the macros section can be inserted in the document.
        """
        section = tool.find_element(XREF)
        if section:
            return tool.get_position_after(section)
        section = tool.find_element(DESCRIPTION)
        if section:
            return tool.get_position_after(section)
        root = tool.find_element(TOOL)
        content_range = tool.get_content_range(root)
        if content_range:
            return content_range.start
        return Position(line=0, character=0)
    def test_analyze_inputs_returns_expected_number_of_leaves(self) -> None:
        tool = GalaxyToolXmlDocument(TEST_TOOL_WITH_INPUTS_DOCUMENT)
        result = tool.analyze_inputs()

        assert len(result.leaves) == 3
    def test_init_sets_properties(self) -> None:
        document = TestUtils.to_document("<tool></tool>")
        tool = GalaxyToolXmlDocument(document)

        assert not tool.xml_document.is_empty
    def test_uses_macros_returns_expected(self, source: str, expected: bool) -> None:
        document = TestUtils.to_document(source)
        tool = GalaxyToolXmlDocument(document)

        assert tool.uses_macros == expected
    def test_is_valid(self, source: str, expected: bool) -> None:
        document = TestUtils.to_document(source)
        tool = GalaxyToolXmlDocument(document)

        assert tool.is_valid == expected