Files
vamp/tests/unit/lsp.rs
T
2026-10-16 18:53:02 +02:00

243 lines
7.9 KiB
Rust

use super::*;
#[test]
fn native_definitions_preserve_snapshot_identity_and_utf16_until_navigation() {
let source = Source {
buffer_id: 7,
revision: 3,
row: 0,
column: 0,
request_id: 11,
};
let path = Path::new("/vamp-immutable-snapshot/Before/renamed/λ.rs");
let span = json!({
"start": {"line": 4, "character": 5},
"end": {"line": 4, "character": 8}
});
for location in [
json!({"uri": file_uri(path).unwrap(), "range": span}),
json!({"targetUri": file_uri(path).unwrap(),
"targetRange": span, "targetSelectionRange": span}),
] {
let effects = Runtime::definition_effects(&source, &location).unwrap();
let expected_target_range = location.get("targetUri").map(|_| Utf16Range {
start: (4, 5),
end: (4, 8),
});
assert!(matches!(
effects.as_slice(),
[UpdateEffect::NavigateUtf16 {
buffer_id: 7, revision: 3, path: target,
range: Utf16Range { start: (4, 5), end: (4, 8) }, target_range,
}] if target == path && *target_range == expected_target_range
));
}
for invalid in [
json!({"start": {"line": 4, "character": 8},
"end": {"line": 4, "character": 5}}),
json!({"start": {"line": -1, "character": 0},
"end": {"line": 0, "character": 0}}),
] {
for location in [
json!({"uri": file_uri(path).unwrap(), "range": invalid}),
json!({"targetUri": file_uri(path).unwrap(),
"targetRange": invalid, "targetSelectionRange": span}),
json!({"targetUri": file_uri(path).unwrap(),
"targetRange": span, "targetSelectionRange": invalid}),
] {
assert!(Runtime::definition_effects(&source, &location).is_err());
}
}
assert!(
serde_json::from_value::<UpdateEffect>(json!({
"type": "navigate_utf16", "buffer_id": 7, "revision": 3,
"path": path, "range": {"start": [4,5], "end": [4,8]},
"target_range": null
}))
.is_err()
);
}
#[test]
fn semantic_decoder_validates_raw_coordinates_before_grapheme_rounding() {
let options = semantic_options(Some(&json!({
"full": true,
"legend": {"tokenTypes": ["custom", "function"],
"tokenModifiers": ["custom", "readonly", "declaration", "async"]}
})))
.unwrap()
.unwrap();
let tokens = decode_semantic_tokens(
"A😀e\u{301}Z\r\n\r\nx",
&json!({"data": [
0, 1, 2, 1, 2,
0, 2, 1, 1, 12,
0, 1, 1, 1, 0,
0, 1, 999, 0, 0,
1, 0, 1, 1, 0,
1, 0, 0, 1, 0,
0, 0, 100, 1, 0
]}),
&options,
)
.unwrap()
.unwrap();
assert_eq!(tokens.len(), 4);
assert_eq!(tokens[0].start, TextPosition { row: 0, column: 1 });
assert_eq!(tokens[0].end, TextPosition { row: 0, column: 2 });
assert_eq!(tokens[0].modifiers, SemanticTokenModifier::Readonly as u16);
assert_eq!(tokens[1].start, TextPosition { row: 0, column: 2 });
assert_eq!(tokens[1].end, TextPosition { row: 0, column: 3 });
assert_eq!(
tokens[1].modifiers,
SemanticTokenModifier::Declaration as u16 | SemanticTokenModifier::Async as u16
);
assert_eq!(tokens[2].start, tokens[1].start);
assert_eq!(tokens[3].end, TextPosition { row: 2, column: 1 });
for data in [
json!([0, 0, 1, 1]),
json!([-1, 0, 1, 1, 0]),
json!([0, 0.5, 1, 1, 0]),
json!([0, 0, 1, 1, 2147483648u64]),
json!([0, 0, 1, 65536, 0]),
json!([0, 0, 1, 2, 0]),
json!([0, 0, 1, 1, 16]),
json!([0, 2, 1, 0, 0]),
json!([0, 1, 1, 1, 0]),
json!([0, 7, 1, 1, 0]),
json!([2, 0, 1, 1, 0]),
json!([0, 1, 2, 1, 0, 0, 1, 1, 1, 0]),
] {
assert!(
decode_semantic_tokens("A😀e\u{301}Z\n", &json!({"data": data}), &options).is_err(),
"{data}"
);
}
let count = MAX_BODY / std::mem::size_of::<SemanticToken>() + 1;
assert!(decode_semantic_tokens("x", &json!({"data": vec![0; count * 5]}), &options).is_err());
}
#[test]
fn unicode_positions_and_local_uri_boundaries() {
let text = "A😀e\u{301}Z\n";
let (start, end) = range(
text,
&json!({"start": {"line": 0, "character": 2}, "end": {"line": 0, "character": 4}}),
)
.unwrap();
assert_eq!(start, TextPosition { row: 0, column: 1 });
assert_eq!(end, TextPosition { row: 0, column: 3 });
assert_eq!(
outbound_position(text, 0, 3).unwrap(),
json!({"line": 0, "character": 5})
);
assert_eq!(
position(text, &json!({"line": 1, "character": 999}), false).unwrap(),
TextPosition { row: 1, column: 0 }
);
assert!(
range(
text,
&json!({"start": {"line": 0, "character": 3}, "end": {"line": 0, "character": 2}})
)
.is_err()
);
let path = Path::new("/tmp/source space λ😀.rs");
assert_eq!(uri_path(&file_uri(path).unwrap()).unwrap(), path);
assert_eq!(
uri_path("file://localhost/tmp/a%23b").unwrap(),
Path::new("/tmp/a#b")
);
for uri in [
"https://example.invalid/a",
"file://remote/tmp/a",
"file:///tmp/%",
"file:///tmp/%00",
"file:///tmp/%FF",
"file:///tmp/a?query",
] {
assert!(uri_path(uri).is_err(), "{uri}");
}
}
#[test]
fn hover_contents_keep_lines_and_reject_malformed_values() {
let source = "A😀e\u{301}Z\r\n";
for kind in ["plaintext", "markdown"] {
assert_eq!(
hover_text(
&json!({
"contents": {"kind": kind, "value": "😀e\u{301}\r\n\tcode\rnext\u{1b}\u{85}"},
"range": {"start": {"line": 0, "character": 2},
"end": {"line": 0, "character": 4}}
}),
source,
)
.unwrap(),
Some("😀e\u{301}\n code\nnext ".into()),
);
}
for contents in [
json!("line\ncode"),
json!({"language": "rust", "value": "line\ncode"}),
] {
assert_eq!(
hover_text(&json!({"contents": contents}), source).unwrap(),
Some("line\ncode".into()),
);
}
assert_eq!(
hover_text(
&json!({"contents": ["first", {"language": "rust", "value": "fn main() {\n}"}]}),
source,
)
.unwrap(),
Some("first\n\nfn main() {\n}".into()),
);
for value in [
Value::Null,
json!({"contents": ""}),
json!({"contents": []}),
] {
assert_eq!(hover_text(&value, source).unwrap(), None);
}
for value in [
json!({}),
json!({"contents": {"kind": "html", "value": "x"}}),
json!({"contents": {"language": "rust", "value": false}}),
json!({"contents": [{"kind": "markdown", "value": "x"}]}),
json!({"contents": "x", "range": {"start": {"line": 1, "character": 0},
"end": {"line": 0, "character": 0}}}),
json!({"contents": "\t".repeat(MAX_BODY / 4 + 1)}),
] {
assert!(hover_text(&value, source).is_err());
}
}
#[test]
fn completion_text_limit_counts_the_atomic_batch_not_sort_metadata() {
let source = Source {
buffer_id: 1,
revision: 0,
row: 0,
column: 0,
request_id: 1,
};
let at_limit = json!([{"label": "x", "insertText": "x".repeat(MAX_BODY - 1)}]);
assert_eq!(
decode_completion("", &at_limit, &source, None).unwrap()[0].edits[0]
.text
.len(),
MAX_BODY - 1
);
let over_limit = json!([
{"label": "x", "insertText": "x".repeat(MAX_BODY - 1)},
{"label": "y"}
]);
assert!(
decode_completion("", &over_limit, &source, None)
.unwrap_err()
.contains("4 MiB")
);
}