test: overhaul initialization logic (#187)

This is a large refactor that separates the document population logic
from the language population logic. This allows us to craft much more
granular tests, such as ones that check for diagnostics when no
languages are found. It also paves the way to using real language
objects in tests, to provide full end-to-end coverage. Additionally, it
allows tests to be fully workspace-aware, allowing coverage for
workspace methods (e.g. workspace symbols), and for diagnostics which
require importing files from the workspace.

It also removes the custom test document population logic with actual
calls to `textDocument/didOpen`, giving full coverage for that
notification.
This commit is contained in:
Riley Bruins 2025-07-09 18:10:37 -07:00 committed by GitHub
parent 3c0871e738
commit ddee107f80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 358 additions and 347 deletions

View file

@ -1 +1,3 @@
; test query
(squid)

View file

@ -345,18 +345,7 @@ mod test {
#[case] expected_code_actions: &[CodeActionOrCommand],
) {
// Arrange
let mut service = initialize_server(
&[(
TEST_URI.clone(),
source,
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
)],
&options,
)
.await;
let mut service = initialize_server(&[(TEST_URI.clone(), source)], &[], &options).await;
// Act
let code_actions = service

View file

@ -750,13 +750,12 @@ the `inherits:` keyword, and there must be no spaces in-between module names.
) {
// Arrange
let mut service = initialize_server(
&[(TEST_URI.clone(), source)],
&[(
TEST_URI.clone(),
source,
String::from("js"),
symbols.to_vec(),
fields.to_vec(),
supertypes.to_vec(),
Vec::new(),
)],
options,
)

View file

@ -721,7 +721,10 @@ fn validate_predicate<'a>(
#[cfg(test)]
mod test {
use std::collections::{BTreeMap, HashMap};
use std::{
collections::{BTreeMap, HashMap},
sync::LazyLock,
};
use tower::{Service as _, ServiceExt as _};
use pretty_assertions::assert_eq;
@ -730,7 +733,7 @@ mod test {
Diagnostic, DiagnosticRelatedInformation, DiagnosticTag, DocumentDiagnosticParams,
DocumentDiagnosticReport, DocumentDiagnosticReportResult, FullDocumentDiagnosticReport,
Location, Position, Range, RelatedFullDocumentDiagnosticReport, TextDocumentIdentifier,
request::DocumentDiagnosticRequest,
Url, request::DocumentDiagnosticRequest,
};
use ts_query_ls::{
DiagnosticOptions, Options, Predicate, PredicateParameter, PredicateParameterArity,
@ -744,22 +747,34 @@ mod test {
diagnostic::{ERROR_SEVERITY, HINT_SEVERITY, WARNING_SEVERITY},
},
test_helpers::helpers::{
Document, TEST_URI, TEST_URI_2, initialize_server, lsp_request_to_jsonrpc_request,
Document, TEST_URI, TestLanguage, initialize_server, lsp_request_to_jsonrpc_request,
lsp_response_to_jsonrpc_response,
},
};
static CPP_FILE_URI: LazyLock<Url> = LazyLock::new(|| {
Url::from_file_path(concat!(
env!("CARGO_MANIFEST_DIR"),
"/queries/test_workspace/queries/cpp/test.scm"
))
.unwrap()
});
#[rstest]
#[case(
&[(
TEST_URI.clone(),
r#"((identifier) @constant
(#match? @cons "^[A-Z][A-Z\\d_]*$"))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
BTreeMap::from([
@ -804,11 +819,15 @@ mod test {
TEST_URI.clone(),
r#"("*" @constant
(#match? @constant "^[A-Z][A-Z\\d_]*$"))"#,
[SymbolInfo { label: String::from("*"), named: false }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("*"), named: false }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
BTreeMap::from([
@ -832,11 +851,15 @@ mod test {
&[(
TEST_URI.clone(),
r#"(MISSING "*") @keyword"#,
[SymbolInfo { label: String::from("*"), named: false }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("*"), named: false }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
BTreeMap::from([
@ -860,11 +883,15 @@ mod test {
&[(
TEST_URI.clone(),
r#"[ "*" ] @keyword"#,
[SymbolInfo { label: String::from("*"), named: false }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("*"), named: false }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
BTreeMap::from([
@ -888,11 +915,15 @@ mod test {
&[(
TEST_URI.clone(),
r#"("*") @keyword"#,
[SymbolInfo { label: String::from("*"), named: false }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("*"), named: false }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
BTreeMap::from([
@ -919,11 +950,15 @@ mod test {
(#match? @_constant "^[A-Z][A-Z\\d_]*$"))
(identifier) @variable"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
BTreeMap::from([
@ -953,11 +988,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable
(#match? @variable "^[A-Z][A-Z\\d_]*$"))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
BTreeMap::from([
@ -973,11 +1012,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#eq? @variable.builtin self))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: BTreeMap::from([(String::from("eq"), Predicate {
description: String::from("Checks for equality"),
@ -1002,11 +1045,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#eq? @variable.builtin))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: BTreeMap::from([(String::from("eq"), Predicate {
description: String::from("Checks for equality"),
@ -1044,11 +1091,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#eq? @variable.builtin self @variable.builtin))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: BTreeMap::from([(String::from("eq"), Predicate {
description: String::from("Checks for equality"),
@ -1086,11 +1137,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#set! @variable.builtin "self" "asdf" bar))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: Default::default(),
valid_directives: BTreeMap::from([(String::from("set"), Predicate {
@ -1116,11 +1171,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#set! @variable.builtin self asdf "bar"))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
diagnostic_options: DiagnosticOptions {
string_argument_style: StringArgumentStyle::PreferUnquoted,
@ -1163,11 +1222,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#set! @variable.builtin self _ "bar"))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
diagnostic_options: DiagnosticOptions {
string_argument_style: StringArgumentStyle::PreferQuoted,
@ -1222,11 +1285,15 @@ mod test {
&[(
TEST_URI.clone(),
r#"(identifier) @_capture"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
diagnostic_options: DiagnosticOptions::default(),
valid_predicates: Default::default(),
@ -1266,11 +1333,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#set! @variable.builtin self asdf bar @variable.builtin))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: Default::default(),
valid_directives: BTreeMap::from([(String::from("set"), Predicate {
@ -1311,11 +1382,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#set! @variable.builtin self asdf bar @variable.builtin))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: Default::default(),
valid_directives: BTreeMap::from([(String::from("set"), Predicate {
@ -1341,11 +1416,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#set! @variable.builtin))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: Default::default(),
valid_directives: BTreeMap::from([(String::from("set"), Predicate {
@ -1371,11 +1450,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#set! @variable.builtin))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: Default::default(),
valid_directives: BTreeMap::from([(String::from("set"), Predicate {
@ -1401,11 +1484,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#set! @variable.builtin self))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: Default::default(),
valid_directives: BTreeMap::from([(String::from("set"), Predicate {
@ -1431,11 +1518,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#set! @variable.builtin self asdf))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: Default::default(),
valid_directives: BTreeMap::from([(String::from("set"), Predicate {
@ -1476,11 +1567,15 @@ mod test {
TEST_URI.clone(),
r#"((identifier) @variable.builtin
(#sett! @variable.builtin self asdf bar @variable.builtin))"#,
[SymbolInfo { label: String::from("identifier"), named: true }].to_vec(),
["operator"].to_vec(),
["supertype"].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from("identifier"), named: true }],
vec!["operator"],
vec!["supertype"],
),
],
Options {
valid_predicates: Default::default(),
valid_directives: BTreeMap::from([(String::from("set"), Predicate {
@ -1520,14 +1615,18 @@ mod test {
&[(
TEST_URI.clone(),
r#""\p" @_cap "\\" @_anothercap "#,
[
SymbolInfo { label: String::from(r"\\"), named: false },
SymbolInfo { label: String::from("p"), named: false }
].to_vec(),
[].to_vec(),
[].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![
SymbolInfo { label: String::from(r"\\"), named: false },
SymbolInfo { label: String::from("p"), named: false }
],
vec![],
vec![],
),
],
Options {
diagnostic_options: DiagnosticOptions { warn_unused_underscore_captures: false, ..Default::default() },
..Default::default()
@ -1553,11 +1652,15 @@ mod test {
&[(
TEST_URI.clone(),
r#"(identifier (identifier) (#set! foo bar))"#,
[SymbolInfo { label: String::from(r"identifier"), named: true }].to_vec(),
[].to_vec(),
[].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from(r"identifier"), named: true }],
vec![],
vec![],
),
],
Options::default(),
&[Diagnostic {
range: Range {
@ -1575,11 +1678,15 @@ mod test {
&[(
TEST_URI.clone(),
r#"(identifier name: (identifier) @capture) (identifier asdf: (identifier) @capture)"#,
[SymbolInfo { label: String::from(r"identifier"), named: true }].to_vec(),
["name"].to_vec(),
[].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from(r"identifier"), named: true }],
vec!["name"],
vec![],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
BTreeMap::from([(String::from("capture"), String::default())]))]),
@ -1599,11 +1706,15 @@ mod test {
&[(
TEST_URI.clone(),
r#"(identifier !asdf) @capture"#,
[SymbolInfo { label: String::from(r"identifier"), named: true }].to_vec(),
["name"].to_vec(),
[].to_vec(),
[].to_vec(),
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from(r"identifier"), named: true }],
vec!["name"],
vec![],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
BTreeMap::from([(String::from("capture"), String::default())]))]),
@ -1622,20 +1733,16 @@ mod test {
#[case(
&[(
TEST_URI.clone(),
r#"; inherits: css
r#"; inherits: cpp
(identifier) @capture"#,
[SymbolInfo { label: String::from(r"identifier"), named: true }].to_vec(),
[].to_vec(),
[].to_vec(),
vec![(10, 13, Some(TEST_URI_2.clone()))],
), (
TEST_URI_2.clone(),
r#"(squid)"#,
[SymbolInfo { label: String::from(r"squid"), named: true }].to_vec(),
[].to_vec(),
[].to_vec(),
[].to_vec(),
)
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from(r"identifier"), named: true }],
vec![],
vec![],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
@ -1644,20 +1751,20 @@ mod test {
},
&[Diagnostic {
message: String::from("Issues in module"),
range: Range::new(Position::new(0, 10), Position::new(0, 13)),
range: Range::new(Position::new(0, 12), Position::new(0, 15)),
severity: ERROR_SEVERITY,
related_information: Some(vec![
DiagnosticRelatedInformation {
location: Location {
uri: TEST_URI_2.clone(),
range: Range::new(Position::new(0, 0), Position::new(0, 7))
uri: CPP_FILE_URI.clone(),
range: Range::new(Position::new(2, 0), Position::new(2, 7))
},
message: String::from("This pattern has no captures, and will not be processed")
},
DiagnosticRelatedInformation {
location: Location {
uri: TEST_URI_2.clone(),
range: Range::new(Position::new(0, 1), Position::new(0, 6))
uri: CPP_FILE_URI.clone(),
range: Range::new(Position::new(2, 1), Position::new(2, 6))
},
message: String::from("Invalid node type: \"squid\"")
},
@ -1668,20 +1775,22 @@ mod test {
#[case(
&[(
TEST_URI.clone(),
r#"; inherits: css
(identifier) @capture"#,
[SymbolInfo { label: String::from(r"identifier"), named: true }].to_vec(),
[].to_vec(),
[].to_vec(),
vec![(10, 13, Some(TEST_URI_2.clone()))],
), (
TEST_URI_2.clone(),
r#"(identifier)"#,
[SymbolInfo { label: String::from(r"squid"), named: true }].to_vec(),
[].to_vec(),
[].to_vec(),
[].to_vec(),
)
r#"; inherits: cpp
(squid) @capture"#,
)],
&[
(
String::from("js"),
vec![SymbolInfo { label: String::from(r"squid"), named: true }],
vec![],
vec![],
),
(
String::from("cpp"),
vec![SymbolInfo { label: String::from(r"identifier"), named: true }],
vec![],
vec![],
),
],
Options {
valid_captures: HashMap::from([(String::from("test"),
@ -1690,13 +1799,13 @@ mod test {
},
&[Diagnostic {
message: String::from("Issues in module"),
range: Range::new(Position::new(0, 10), Position::new(0, 13)),
range: Range::new(Position::new(0, 12), Position::new(0, 15)),
severity: WARNING_SEVERITY,
related_information: Some(vec![
DiagnosticRelatedInformation {
location: Location {
uri: TEST_URI_2.clone(),
range: Range::new(Position::new(0, 0), Position::new(0, 12))
uri: CPP_FILE_URI.clone(),
range: Range::new(Position::new(2, 0), Position::new(2, 7))
},
message: String::from("This pattern has no captures, and will not be processed")
},
@ -1707,11 +1816,12 @@ mod test {
#[tokio::test(flavor = "current_thread")]
async fn server_diagnostics(
#[case] documents: &[Document<'_>],
#[case] languages: &[TestLanguage<'_>],
#[case] options: Options,
#[case] expected_diagnostics: &[Diagnostic],
) {
// Arrange
let mut service = initialize_server(documents, &options).await;
let mut service = initialize_server(documents, languages, &options).await;
// Act
let actual_diagnostics = service

View file

@ -123,18 +123,8 @@ mod test {
#[case] edits: &[TestEdit],
) {
// Arrange
let mut service = initialize_server(
&[(
TEST_URI.clone(),
original,
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
)],
&Default::default(),
)
.await;
let mut service =
initialize_server(&[(TEST_URI.clone(), original)], &[], &Default::default()).await;
// Act
service

View file

@ -33,7 +33,7 @@ mod test {
#[tokio::test(flavor = "current_thread")]
async fn server_did_change_configuration() {
// Arrange
let mut service = initialize_server(&[], &Default::default()).await;
let mut service = initialize_server(&[], &[], &Default::default()).await;
// Act
service

View file

@ -185,7 +185,7 @@ mod test {
#[tokio::test(flavor = "current_thread")]
async fn server_did_open_document() {
// Arrange
let mut service = initialize_server(&[], &Default::default()).await;
let mut service = initialize_server(&[], &[], &Default::default()).await;
let source = r#""[" @cap"#;
// Act

View file

@ -192,18 +192,8 @@ expression: (boolean) @boolean",
#[case] highlights: &[Highlight],
) {
// Arrange
let mut service = initialize_server(
&[(
TEST_URI.clone(),
input,
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
)],
&Default::default(),
)
.await;
let mut service =
initialize_server(&[(TEST_URI.clone(), input)], &[], &Default::default()).await;
// Act
let refs = service

View file

@ -140,18 +140,8 @@ mod test {
#[tokio::test(flavor = "current_thread")]
async fn document_symbol(#[case] source: &str, #[case] symbols: Vec<DocSymbol>) {
// Arrange
let mut service = initialize_server(
&[(
TEST_URI.clone(),
source,
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
)],
&Default::default(),
)
.await;
let mut service =
initialize_server(&[(TEST_URI.clone(), source)], &[], &Default::default()).await;
// Act
let tokens = service

View file

@ -362,18 +362,8 @@ mod test {
#[tokio::test(flavor = "current_thread")]
async fn server_formatting(#[case] before: &str, #[case] after: &str) {
// Arrange
let mut service = initialize_server(
&[(
TEST_URI.clone(),
before,
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
)],
&Default::default(),
)
.await;
let mut service =
initialize_server(&[(TEST_URI.clone(), before)], &[], &Default::default()).await;
// Act
let delta = service

View file

@ -150,18 +150,8 @@ mod test {
#[case] locations: (Url, &[Coordinate]),
) {
// Arrange
let mut service = initialize_server(
&[(
TEST_URI.clone(),
input,
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
)],
&Default::default(),
)
.await;
let mut service =
initialize_server(&[(TEST_URI.clone(), input)], &[], &Default::default()).await;
// Act
let refs = service

View file

@ -397,14 +397,8 @@ An error node", BTreeMap::from([(String::from("error"), String::from("An error n
) {
// Arrange
let mut service = initialize_server(
&[(
TEST_URI.clone(),
source,
Vec::new(),
Vec::new(),
supertypes,
Vec::new(),
)],
&[(TEST_URI.clone(), source)],
&[(String::from("js"), Vec::new(), Vec::new(), supertypes)],
&Options {
valid_captures: HashMap::from([(String::from("test"), captures)]),
valid_predicates: BTreeMap::from([(

View file

@ -125,18 +125,8 @@ function: (identifier) @function)",
#[case] ranges: &[Coordinate],
) {
// Arrange
let mut service = initialize_server(
&[(
TEST_URI.clone(),
input,
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
)],
&Default::default(),
)
.await;
let mut service =
initialize_server(&[(TEST_URI.clone(), input)], &[], &Default::default()).await;
// Act
let refs = service

View file

@ -131,18 +131,8 @@ mod test {
#[case] new_name: &str,
) {
// Arrange
let mut service = initialize_server(
&[(
TEST_URI.clone(),
original,
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
)],
&Default::default(),
)
.await;
let mut service =
initialize_server(&[(TEST_URI.clone(), original)], &[], &Default::default()).await;
// Act
let rename_edits = service

View file

@ -107,14 +107,8 @@ mod test {
) {
// Arrange
let mut service = initialize_server(
&[(
TEST_URI.clone(),
document_text,
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
)],
&[(TEST_URI.clone(), document_text)],
&[],
&Default::default(),
)
.await;

View file

@ -224,13 +224,12 @@ mod test {
(foo)
";
let mut service = initialize_server(
&[(TEST_URI.clone(), source)],
&[(
TEST_URI.clone(),
source,
String::from("js"),
Vec::new(),
Vec::new(),
vec!["supertype"],
Vec::new(),
)],
&Default::default(),
)

View file

@ -122,7 +122,7 @@ struct DocumentData {
imported_uris: Vec<(u32, u32, Option<Url>)>,
}
#[derive(Default, Debug)]
#[derive(Clone, Default, Debug)]
struct LanguageData {
name: String,
symbols_set: HashSet<SymbolInfo>,

View file

@ -1,6 +1,5 @@
#[cfg(test)]
pub mod helpers {
use ropey::Rope;
use serde_json::to_value;
use std::{
@ -17,15 +16,13 @@ pub mod helpers {
LspService,
jsonrpc::{Request, Response},
lsp_types::{
ClientCapabilities, InitializeParams, Position, Range, TextDocumentContentChangeEvent,
TextEdit, Url, request::Initialize,
ClientCapabilities, DidOpenTextDocumentParams, InitializeParams, Position, Range,
TextDocumentContentChangeEvent, TextDocumentItem, TextEdit, Url,
notification::DidOpenTextDocument, request::Initialize,
},
};
use crate::{
Backend, DocumentData, LanguageData, Options, QUERY_LANGUAGE, SymbolInfo,
util::get_language_name,
};
use crate::{Backend, LanguageData, Options, QUERY_LANGUAGE, SymbolInfo};
pub static TEST_URI: LazyLock<Url> =
LazyLock::new(|| Url::parse("file:///tmp/queries/js/test.scm").unwrap());
@ -43,20 +40,16 @@ pub mod helpers {
/// Always test with id of 1 for simplicity
const ID: i64 = 1;
/// A tuple holding the document's URI, source text, symbols, fields, supertypes, and valid
/// captures
pub type Document<'a> = (
Url,
&'a str,
Vec<SymbolInfo>,
Vec<&'a str>,
Vec<&'a str>,
Vec<(u32, u32, Option<Url>)>,
);
/// A tuple holding the document's URI and source text.
pub type Document<'a> = (Url, &'a str);
/// A tuple holding the language's name, symbols, fields, and supertype names.
pub type TestLanguage<'a> = (String, Vec<SymbolInfo>, Vec<&'a str>, Vec<&'a str>);
/// Initialize a test server, populating it with fake documents denoted by (uri, text, symbols, fields) tuples.
pub async fn initialize_server(
documents: &[Document<'_>],
languages: &[TestLanguage<'_>],
options: &Options,
) -> LspService<Backend> {
let mut parser = Parser::new();
@ -66,67 +59,50 @@ pub mod helpers {
let options_value = serde_json::to_value(options).unwrap();
let options = &serde_json::from_value::<Options>(options_value.clone()).unwrap();
let arced_options = Arc::new(tokio::sync::RwLock::new(options.clone()));
let workspace_dirs = vec![
PathBuf::from_str(concat!(
env!("CARGO_MANIFEST_DIR"),
"/queries/test_workspace/"
))
.unwrap(),
];
let (mut service, _socket) = LspService::build(|client| Backend {
_client: client,
document_map: DashMap::from_iter(documents.iter().map(
|(uri, source, _, _, _, imported_uris)| {
document_map: Default::default(),
language_map: DashMap::from_iter(languages.iter().cloned().map(
|(name, symbols, fields, supertypes)| {
(
uri.clone(),
DocumentData {
rope: Rope::from(*source),
tree: parser.parse(*source, None).unwrap(),
version: 0,
language_name: get_language_name(uri, options),
imported_uris: imported_uris.clone(),
},
)
},
)),
language_map: DashMap::from_iter(documents.iter().map(
|(uri, _, symbols, fields, supertypes, _)| {
let language_name = get_language_name(uri, options).unwrap();
(
language_name.clone(),
LanguageData {
name: language_name,
language: None,
symbols_set: HashSet::from_iter(symbols.clone()),
symbols_vec: symbols.clone(),
name.clone(),
Arc::new(LanguageData {
name,
symbols_set: HashSet::from_iter(symbols.iter().cloned()),
symbols_vec: symbols.to_vec(),
fields_set: HashSet::from_iter(fields.iter().map(ToString::to_string)),
fields_vec: fields.clone().iter().map(ToString::to_string).collect(),
supertype_map: HashMap::from_iter(supertypes.iter().map(|supertype| {
fields_vec: fields.iter().map(ToString::to_string).collect(),
supertype_map: HashMap::from_iter(supertypes.iter().map(|st| {
(
SymbolInfo {
label: st.to_string(),
named: true,
label: String::from(*supertype),
},
BTreeSet::from([
SymbolInfo {
label: "test".to_string(),
named: true,
label: String::from("test"),
},
SymbolInfo {
label: "test2".to_string(),
named: true,
label: String::from("test2"),
},
]),
)
})),
}
.into(),
language: None,
}),
)
},
)),
workspace_uris: Arc::new(
vec![
PathBuf::from_str(concat!(
env!("CARGO_MANIFEST_DIR"),
"/queries/test_workspace/"
))
.unwrap(),
]
.into(),
),
workspace_uris: Arc::new(workspace_dirs.into()),
options: arced_options,
})
.finish();
@ -147,6 +123,26 @@ pub mod helpers {
.unwrap()
.unwrap();
// Open the documents
for (uri, src) in documents.iter().cloned() {
service
.ready()
.await
.unwrap()
.call(lsp_notification_to_jsonrpc_request::<DidOpenTextDocument>(
DidOpenTextDocumentParams {
text_document: TextDocumentItem {
version: 0,
language_id: String::from("query"),
text: src.to_string(),
uri,
},
},
))
.await
.unwrap();
}
service
}
@ -244,34 +240,31 @@ mod test {
test_helpers::helpers::{
COMPLEX_FILE, SIMPLE_FILE, TEST_URI, TEST_URI_2, initialize_server,
},
util::get_language_name,
};
use super::helpers::Document;
use super::helpers::{Document, TestLanguage};
#[rstest]
#[case(&[], &Default::default())]
#[case(&[], &[], &Default::default())]
#[case(&[(
TEST_URI.clone(),
SIMPLE_FILE,
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
), (
TEST_URI_2.clone(),
COMPLEX_FILE,
vec![
SymbolInfo { named: true, label: String::from("identifier") },
SymbolInfo { named: false, label: String::from(";") }
TEST_URI.clone(),
SIMPLE_FILE,
),
(
TEST_URI_2.clone(),
COMPLEX_FILE,
)],
&[
(
String::from("css"),
vec![
SymbolInfo { named: true, label: String::from("identifier") },
SymbolInfo { named: false, label: String::from(";") },
],
vec!["operator", "content"],
vec!["type"]
)
],
vec![
"operator",
"content",
],
vec!["type"],
vec![(0, 2, None)],
)],
&Options {
valid_captures: HashMap::from([(String::from("test"), BTreeMap::from([(String::from("variable"), String::from("A common variable"))]))]),
..Default::default()
@ -280,10 +273,11 @@ mod test {
#[tokio::test(flavor = "current_thread")]
async fn initialize_server_helper(
#[case] documents: &[Document<'_>],
#[case] languages: &[TestLanguage<'_>],
#[case] options: &Options,
) {
// Act
let service = initialize_server(documents, options).await;
let service = initialize_server(documents, languages, options).await;
// Assert
let backend = service.inner();
@ -295,7 +289,7 @@ mod test {
let actual_options = backend.options.read().await;
assert_eq!(actual_options.deref(), options);
assert_eq!(backend.document_map.len(), documents.len());
for (uri, source, symbols, fields, supertypes, imported_urls) in documents {
for (uri, source) in documents {
let doc = backend.document_map.get(uri).unwrap();
assert_eq!(doc.rope.to_string(), (*source).to_string());
assert_eq!(
@ -305,8 +299,9 @@ mod test {
.unwrap(),
(*source).to_string()
);
let language_name = get_language_name(uri, options).unwrap();
let language_data = backend.language_map.get(&language_name).unwrap();
}
for (language_name, symbols, fields, supertypes) in languages {
let language_data = backend.language_map.get(language_name).unwrap();
assert!(language_data.symbols_vec.len() == symbols.len());
assert!(language_data.symbols_set.len() == symbols.len());
for symbol in symbols {
@ -325,7 +320,6 @@ mod test {
label: String::from(*supertype)
}))
}
assert_eq!(imported_urls, &doc.imported_uris);
}
}
}