Skip to content

Commit f2ff77a

Browse files
SoonIterHerringtonDarkholme
authored andcommittedApr 3, 2024
test: move the integration-test to test folder
1 parent ff5f300 commit f2ff77a

File tree

2 files changed

+171
-151
lines changed

2 files changed

+171
-151
lines changed
 

‎crates/lsp/src/lib.rs

+15-151
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,19 @@ fn url_to_code_description(url: &Option<String>) -> Option<CodeDescription> {
289289
Some(CodeDescription { href })
290290
}
291291

292+
fn build_error_id_to_ranges(diagnostics: Vec<Diagnostic>) -> HashMap<String, Vec<Range>> {
293+
let mut error_id_to_ranges = HashMap::new();
294+
for diagnostic in diagnostics {
295+
let rule_id = match diagnostic.code {
296+
Some(NumberOrString::String(rule)) => rule,
297+
_ => continue,
298+
};
299+
let ranges = error_id_to_ranges.entry(rule_id).or_insert_with(Vec::new);
300+
ranges.push(diagnostic.range);
301+
}
302+
error_id_to_ranges
303+
}
304+
292305
impl<L: LSPLang> Backend<L> {
293306
pub fn new(client: Client, rules: std::result::Result<RuleCollection<L>, String>) -> Self {
294307
Self {
@@ -432,7 +445,7 @@ impl<L: LSPLang> Backend<L> {
432445
let text_doc = params.text_document;
433446
let path = text_doc.uri.to_file_path().ok()?;
434447
let diagnostics = params.context.diagnostics;
435-
let error_id_to_ranges = Self::build_error_id_to_ranges(diagnostics);
448+
let error_id_to_ranges = build_error_id_to_ranges(diagnostics);
436449
let mut response = CodeActionResponse::new();
437450

438451
let code_action = params.context.only.as_ref()?.first()?.clone();
@@ -477,19 +490,6 @@ impl<L: LSPLang> Backend<L> {
477490
Some(response)
478491
}
479492

480-
fn build_error_id_to_ranges(diagnostics: Vec<Diagnostic>) -> HashMap<String, Vec<Range>> {
481-
let mut error_id_to_ranges = HashMap::new();
482-
for diagnostic in diagnostics {
483-
let rule_id = match diagnostic.code {
484-
Some(NumberOrString::String(rule)) => rule,
485-
_ => continue,
486-
};
487-
let ranges = error_id_to_ranges.entry(rule_id).or_insert_with(Vec::new);
488-
ranges.push(diagnostic.range);
489-
}
490-
error_id_to_ranges
491-
}
492-
493493
// TODO: support other urls besides file_scheme
494494
fn infer_lang_from_uri(uri: &Url) -> Option<L> {
495495
let path = uri.to_file_path().ok()?;
@@ -525,7 +525,7 @@ impl<L: LSPLang> Backend<L> {
525525
let versioned = VersionedAst { version, root };
526526

527527
let diagnostics = self.get_diagnostics(&uri, &versioned).await?;
528-
let error_id_to_ranges = Self::build_error_id_to_ranges(diagnostics);
528+
let error_id_to_ranges = build_error_id_to_ranges(diagnostics);
529529
self
530530
.client
531531
.log_message(MessageType::LOG, "Parsing doc.")
@@ -548,139 +548,3 @@ impl<L: LSPLang> Backend<L> {
548548
}
549549
}
550550
}
551-
552-
#[cfg(test)]
553-
mod test {
554-
use super::*;
555-
use ast_grep_config::{from_yaml_string, GlobalRules};
556-
use ast_grep_language::SupportLang;
557-
use serde_json::Value;
558-
use tokio::io::{duplex, AsyncReadExt, AsyncWriteExt, DuplexStream};
559-
560-
fn start_lsp() -> (DuplexStream, DuplexStream) {
561-
let globals = GlobalRules::default();
562-
let config: RuleConfig<SupportLang> = from_yaml_string(
563-
r"
564-
id: no-console-rule
565-
message: No console.log
566-
severity: warning
567-
language: TypeScript
568-
rule:
569-
pattern: console.log($$$A)
570-
note: no console.log
571-
fix: |
572-
alert($$$A)
573-
",
574-
&globals,
575-
)
576-
.unwrap()
577-
.pop()
578-
.unwrap();
579-
let rc: RuleCollection<SupportLang> = RuleCollection::try_new(vec![config]).unwrap();
580-
let rc_result: std::result::Result<_, String> = Ok(rc);
581-
let (service, socket) = LspService::build(|client| Backend::new(client, rc_result)).finish();
582-
let (req_client, req_server) = duplex(1024);
583-
let (resp_server, resp_client) = duplex(1024);
584-
585-
// start server as concurrent task
586-
tokio::spawn(Server::new(req_server, resp_server, socket).serve(service));
587-
588-
(req_client, resp_client)
589-
}
590-
591-
fn req(msg: &str) -> String {
592-
format!("Content-Length: {}\r\n\r\n{}", msg.len(), msg)
593-
}
594-
595-
// A function that takes a byte slice as input and returns the content length as an option
596-
fn resp(input: &[u8]) -> Option<&str> {
597-
let input_str = std::str::from_utf8(input).ok()?;
598-
let mut splits = input_str.split("\r\n\r\n");
599-
let header = splits.next()?;
600-
let body = splits.next()?;
601-
let length_str = header.trim_start_matches("Content-Length: ");
602-
let length = length_str.parse::<usize>().ok()?;
603-
Some(&body[..length])
604-
}
605-
606-
async fn test_lsp() {
607-
let initialize = r#"{
608-
"jsonrpc":"2.0",
609-
"id": 1,
610-
"method": "initialize",
611-
"params": {
612-
"capabilities": {
613-
"textDocumentSync": 1
614-
}
615-
}
616-
}"#;
617-
let (mut req_client, mut resp_client) = start_lsp();
618-
let mut buf = vec![0; 1024];
619-
620-
req_client
621-
.write_all(req(initialize).as_bytes())
622-
.await
623-
.unwrap();
624-
let _ = resp_client.read(&mut buf).await.unwrap();
625-
626-
assert!(resp(&buf).unwrap().starts_with('{'));
627-
628-
let save_file = r#"{
629-
"jsonrpc": "2.0",
630-
"id": 1,
631-
"method": "textDocument/codeAction",
632-
"params": {
633-
"range": {
634-
"end": {
635-
"character": 10,
636-
"line": 1
637-
},
638-
"start": {
639-
"character": 10,
640-
"line": 1
641-
}
642-
},
643-
"textDocument": {
644-
"uri": "file:///Users/codes/ast-grep-vscode/test.tsx"
645-
},
646-
"context": {
647-
"diagnostics": [
648-
{
649-
"range": {
650-
"start": {
651-
"line": 0,
652-
"character": 0
653-
},
654-
"end": {
655-
"line": 0,
656-
"character": 16
657-
}
658-
},
659-
"code": "no-console-rule",
660-
"source": "ast-grep",
661-
"message": "No console.log"
662-
}
663-
],
664-
"only": ["source.fixAll"]
665-
}
666-
}
667-
}"#;
668-
669-
let mut buf = vec![0; 1024];
670-
req_client
671-
.write_all(req(save_file).as_bytes())
672-
.await
673-
.unwrap();
674-
let _ = resp_client.read(&mut buf).await.unwrap();
675-
676-
let json_val: Value = serde_json::from_str(resp(&buf).unwrap()).unwrap();
677-
678-
// {"jsonrpc":"2.0","method":"window/logMessage","params":{"message":"run code action!","type":3}}
679-
assert_eq!(json_val["method"], "window/logMessage");
680-
}
681-
682-
#[test]
683-
fn actual_test() {
684-
tokio::runtime::Runtime::new().unwrap().block_on(test_lsp());
685-
}
686-
}

‎crates/lsp/tests/basic.rs

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
use ast_grep_config::{from_yaml_string, GlobalRules, RuleCollection, RuleConfig};
2+
use ast_grep_language::SupportLang;
3+
use ast_grep_lsp::*;
4+
use serde_json::Value;
5+
use tokio::io::{duplex, AsyncReadExt, AsyncWriteExt, DuplexStream};
6+
7+
pub fn req(msg: &str) -> String {
8+
format!("Content-Length: {}\r\n\r\n{}", msg.len(), msg)
9+
}
10+
11+
// A function that takes a byte slice as input and returns the content length as an option
12+
pub fn resp(input: &[u8]) -> Option<&str> {
13+
let input_str = std::str::from_utf8(input).ok()?;
14+
let mut splits = input_str.split("\r\n\r\n");
15+
let header = splits.next()?;
16+
let body = splits.next()?;
17+
let length_str = header.trim_start_matches("Content-Length: ");
18+
let length = length_str.parse::<usize>().ok()?;
19+
Some(&body[..length])
20+
}
21+
22+
pub fn create_lsp() -> (DuplexStream, DuplexStream) {
23+
let globals = GlobalRules::default();
24+
let config: RuleConfig<SupportLang> = from_yaml_string(
25+
r"
26+
id: no-console-rule
27+
message: No console.log
28+
severity: warning
29+
language: TypeScript
30+
rule:
31+
pattern: console.log($$$A)
32+
note: no console.log
33+
fix: |
34+
alert($$$A)
35+
",
36+
&globals,
37+
)
38+
.unwrap()
39+
.pop()
40+
.unwrap();
41+
let rc: RuleCollection<SupportLang> = RuleCollection::try_new(vec![config]).unwrap();
42+
let rc_result: std::result::Result<_, String> = Ok(rc);
43+
let (service, socket) = LspService::build(|client| Backend::new(client, rc_result)).finish();
44+
let (req_client, req_server) = duplex(1024);
45+
let (resp_server, resp_client) = duplex(1024);
46+
47+
// start server as concurrent task
48+
tokio::spawn(Server::new(req_server, resp_server, socket).serve(service));
49+
50+
(req_client, resp_client)
51+
}
52+
53+
pub async fn initialize_lsp(
54+
req_client: &mut DuplexStream,
55+
resp_client: &mut DuplexStream,
56+
) -> Vec<u8> {
57+
let initialize = r#"{
58+
"jsonrpc":"2.0",
59+
"id": 1,
60+
"method": "initialize",
61+
"params": {
62+
"capabilities": {
63+
"textDocumentSync": 1
64+
}
65+
}
66+
}"#;
67+
let mut buf = vec![0; 1024];
68+
69+
req_client
70+
.write_all(req(initialize).as_bytes())
71+
.await
72+
.unwrap();
73+
let _ = resp_client.read(&mut buf).await.unwrap();
74+
75+
buf
76+
}
77+
78+
pub async fn request_code_action_to_lsp(
79+
req_client: &mut DuplexStream,
80+
resp_client: &mut DuplexStream,
81+
) -> Vec<u8> {
82+
let code_action_request = r#"{
83+
"jsonrpc": "2.0",
84+
"id": 1,
85+
"method": "textDocument/codeAction",
86+
"params": {
87+
"range": {
88+
"end": {
89+
"character": 10,
90+
"line": 1
91+
},
92+
"start": {
93+
"character": 10,
94+
"line": 1
95+
}
96+
},
97+
"textDocument": {
98+
"uri": "file:///Users/codes/ast-grep-vscode/test.tsx"
99+
},
100+
"context": {
101+
"diagnostics": [
102+
{
103+
"range": {
104+
"start": {
105+
"line": 0,
106+
"character": 0
107+
},
108+
"end": {
109+
"line": 0,
110+
"character": 16
111+
}
112+
},
113+
"code": "no-console-rule",
114+
"source": "ast-grep",
115+
"message": "No console.log"
116+
}
117+
],
118+
"only": ["source.fixAll"]
119+
}
120+
}
121+
}"#;
122+
123+
let mut buf = vec![0; 1024];
124+
req_client
125+
.write_all(req(code_action_request).as_bytes())
126+
.await
127+
.unwrap();
128+
let _ = resp_client.read(&mut buf).await.unwrap();
129+
130+
buf
131+
}
132+
133+
#[test]
134+
fn test_basic() {
135+
tokio::runtime::Runtime::new().unwrap().block_on(async {
136+
let (mut req_client, mut resp_client) = create_lsp();
137+
138+
let buf = initialize_lsp(&mut req_client, &mut resp_client).await;
139+
assert!(resp(&buf).unwrap().starts_with('{'));
140+
});
141+
}
142+
143+
#[test]
144+
fn test_code_action() {
145+
tokio::runtime::Runtime::new().unwrap().block_on(async {
146+
let (mut req_client, mut resp_client) = create_lsp();
147+
148+
let buf = initialize_lsp(&mut req_client, &mut resp_client).await;
149+
assert!(resp(&buf).unwrap().starts_with('{'));
150+
151+
let buf = request_code_action_to_lsp(&mut req_client, &mut resp_client).await;
152+
let json_val: Value = serde_json::from_str(resp(&buf).unwrap()).unwrap();
153+
// {"jsonrpc":"2.0","method":"window/logMessage","params":{"message":"run code action!","type":3}}
154+
assert_eq!(json_val["method"], "window/logMessage");
155+
});
156+
}

0 commit comments

Comments
 (0)
Please sign in to comment.