summaryrefslogtreecommitdiffstats
path: root/tests/python/test_cattrs_special_cases.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/python/test_cattrs_special_cases.py')
-rw-r--r--tests/python/test_cattrs_special_cases.py306
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)
+ )