diff options
Diffstat (limited to 'tests/python/test_cattrs_special_cases.py')
-rw-r--r-- | tests/python/test_cattrs_special_cases.py | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/tests/python/test_cattrs_special_cases.py b/tests/python/test_cattrs_special_cases.py new file mode 100644 index 0000000..c5e0e9c --- /dev/null +++ b/tests/python/test_cattrs_special_cases.py @@ -0,0 +1,306 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +from typing import Optional, Union + +import attrs +import hamcrest +import pytest +from cattrs.errors import ClassValidationError + +from lsprotocol import converters as cv +from lsprotocol import types as lsp + + +def test_simple(): + """Ensure that simple LSP types are serializable.""" + data = { + "range": { + "start": {"line": 0, "character": 0}, + "end": {"line": 0, "character": 0}, + }, + "message": "Missing module docstring", + "severity": 3, + "code": "C0114:missing-module-docstring", + "source": "my_lint", + } + converter = cv.get_converter() + obj = converter.structure(data, lsp.Diagnostic) + hamcrest.assert_that(obj, hamcrest.instance_of(lsp.Diagnostic)) + + +def test_numeric_validation(): + """Ensure that out of range numbers raise exception.""" + data = {"line": -1, "character": 0} + converter = cv.get_converter() + with pytest.raises((ClassValidationError, ValueError)): + converter.structure(data, lsp.Position) + + +def test_forward_refs(): + """Test that forward references are handled correctly by cattrs converter.""" + data = { + "uri": "something.py", + "diagnostics": [ + { + "range": { + "start": {"line": 0, "character": 0}, + "end": {"line": 0, "character": 0}, + }, + "message": "Missing module docstring", + "severity": 3, + "code": "C0114:missing-module-docstring", + "source": "my_lint", + }, + { + "range": { + "start": {"line": 2, "character": 6}, + "end": { + "line": 2, + "character": 7, + }, + }, + "message": "Undefined variable 'x'", + "severity": 1, + "code": "E0602:undefined-variable", + "source": "my_lint", + }, + { + "range": { + "start": {"line": 0, "character": 0}, + "end": { + "line": 0, + "character": 10, + }, + }, + "message": "Unused import sys", + "severity": 2, + "code": "W0611:unused-import", + "source": "my_lint", + }, + ], + } + converter = cv.get_converter() + obj = converter.structure(data, lsp.PublishDiagnosticsParams) + hamcrest.assert_that(obj, hamcrest.instance_of(lsp.PublishDiagnosticsParams)) + + +@pytest.mark.parametrize( + "data", + [ + {}, # No properties provided + {"documentSelector": None}, + {"documentSelector": []}, + {"documentSelector": [{"pattern": "something/**"}]}, + {"documentSelector": [{"language": "python"}]}, + {"documentSelector": [{"scheme": "file"}]}, + {"documentSelector": [{"notebook": "jupyter"}]}, + {"documentSelector": [{"language": "python"}]}, + {"documentSelector": [{"notebook": {"notebookType": "jupyter-notebook"}}]}, + {"documentSelector": [{"notebook": {"scheme": "file"}}]}, + {"documentSelector": [{"notebook": {"pattern": "something/**"}}]}, + { + "documentSelector": [ + {"pattern": "something/**"}, + {"language": "python"}, + {"scheme": "file"}, + {"scheme": "untitled", "language": "python"}, + {"notebook": {"pattern": "something/**"}}, + {"notebook": {"scheme": "untitled"}}, + {"notebook": {"notebookType": "jupyter-notebook"}}, + { + "notebook": {"notebookType": "jupyter-notebook"}, + "language": "jupyter", + }, + ] + }, + ], +) +def test_union_with_complex_type(data): + """Ensure types with multiple possible resolutions are handled correctly.""" + converter = cv.get_converter() + obj = converter.structure(data, lsp.TextDocumentRegistrationOptions) + hamcrest.assert_that(obj, hamcrest.instance_of(lsp.TextDocumentRegistrationOptions)) + + +def test_keyword_field(): + """Ensure that fields same names as keywords are handled correctly.""" + data = { + "from": { + "name": "something", + "kind": 5, + "uri": "something.py", + "range": { + "start": {"line": 0, "character": 0}, + "end": { + "line": 0, + "character": 10, + }, + }, + "selectionRange": { + "start": {"line": 0, "character": 2}, + "end": { + "line": 0, + "character": 8, + }, + }, + "data": {"something": "some other"}, + }, + "fromRanges": [ + { + "start": {"line": 0, "character": 0}, + "end": { + "line": 0, + "character": 10, + }, + }, + { + "start": {"line": 12, "character": 0}, + "end": { + "line": 13, + "character": 0, + }, + }, + ], + } + + converter = cv.get_converter() + obj = converter.structure(data, lsp.CallHierarchyIncomingCall) + hamcrest.assert_that(obj, hamcrest.instance_of(lsp.CallHierarchyIncomingCall)) + rev = converter.unstructure(obj, lsp.CallHierarchyIncomingCall) + hamcrest.assert_that(rev, hamcrest.is_(data)) + + +@pytest.mark.parametrize( + "data", + [ + {"settings": None}, + {"settings": 100000}, + {"settings": 1.23456}, + {"settings": True}, + {"settings": "something"}, + {"settings": {"something": "something"}}, + {"settings": []}, + {"settings": [None, None]}, + {"settings": [None, 1, 1.23, True]}, + ], +) +def test_LSPAny(data): + """Ensure that broad primitive and custom type alias is handled correctly.""" + converter = cv.get_converter() + obj = converter.structure(data, lsp.DidChangeConfigurationParams) + hamcrest.assert_that(obj, hamcrest.instance_of(lsp.DidChangeConfigurationParams)) + hamcrest.assert_that( + converter.unstructure(obj, lsp.DidChangeConfigurationParams), + hamcrest.is_(data), + ) + + +@pytest.mark.parametrize( + "data", + [ + {"label": "hi"}, + {"label": [0, 42]}, + ], +) +def test_ParameterInformation(data): + converter = cv.get_converter() + obj = converter.structure(data, lsp.ParameterInformation) + hamcrest.assert_that(obj, hamcrest.instance_of(lsp.ParameterInformation)) + hamcrest.assert_that( + converter.unstructure(obj, lsp.ParameterInformation), + hamcrest.is_(data), + ) + + +def test_completion_item(): + data = dict(label="example", documentation="This is documented") + converter = cv.get_converter() + obj = converter.structure(data, lsp.CompletionItem) + hamcrest.assert_that(obj, hamcrest.instance_of(lsp.CompletionItem)) + hamcrest.assert_that( + converter.unstructure(obj, lsp.CompletionItem), + hamcrest.is_(data), + ) + + +def test_notebook_change_event(): + data = { + "notebookDocument": { + "uri": "untitled:Untitled-1.ipynb?jupyter-notebook", + "notebookType": "jupyter-notebook", + "version": 0, + "cells": [ + { + "kind": 2, + "document": "vscode-notebook-cell:Untitled-1.ipynb?jupyter-notebook#W0sdW50aXRsZWQ%3D", + "metadata": {"custom": {"metadata": {}}}, + } + ], + "metadata": { + "custom": { + "cells": [], + "metadata": { + "orig_nbformat": 4, + "language_info": {"name": "python"}, + }, + }, + "indentAmount": " ", + }, + }, + "cellTextDocuments": [ + { + "uri": "vscode-notebook-cell:Untitled-1.ipynb?jupyter-notebook#W0sdW50aXRsZWQ%3D", + "languageId": "python", + "version": 1, + "text": "", + } + ], + } + + converter = cv.get_converter() + obj = converter.structure(data, lsp.DidOpenNotebookDocumentParams) + hamcrest.assert_that(obj, hamcrest.instance_of(lsp.DidOpenNotebookDocumentParams)) + hamcrest.assert_that( + converter.unstructure(obj, lsp.DidOpenNotebookDocumentParams), + hamcrest.is_(data), + ) + + +def test_notebook_sync_options(): + data = {"notebookSelector": [{"cells": [{"language": "python"}]}]} + + converter = cv.get_converter() + obj = converter.structure(data, lsp.NotebookDocumentSyncOptions) + hamcrest.assert_that(obj, hamcrest.instance_of(lsp.NotebookDocumentSyncOptions)) + hamcrest.assert_that( + converter.unstructure(obj, lsp.NotebookDocumentSyncOptions), + hamcrest.is_(data), + ) + + +@attrs.define +class TestPosEncoding: + """Defines the capabilities provided by a language + server.""" + + position_encoding: Optional[Union[lsp.PositionEncodingKind, str]] = attrs.field( + default=None + ) + + +@pytest.mark.parametrize("e", [None, "utf-8", "utf-16", "utf-32", "something"]) +def test_position_encoding_kind(e): + data = {"positionEncoding": e} + converter = cv.get_converter() + obj = converter.structure(data, TestPosEncoding) + hamcrest.assert_that(obj, hamcrest.instance_of(TestPosEncoding)) + + if e is None: + hamcrest.assert_that( + converter.unstructure(obj, TestPosEncoding), hamcrest.is_({}) + ) + else: + hamcrest.assert_that( + converter.unstructure(obj, TestPosEncoding), hamcrest.is_(data) + ) |