Skip to content

Commit d3dacc9

Browse files
committed
ci(benchmark): add language_server linter benchmark
1 parent 2fcc079 commit d3dacc9

File tree

6 files changed

+121
-25
lines changed

6 files changed

+121
-25
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/oxc_language_server/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,15 @@ papaya = { workspace = true }
3838
rustc-hash = { workspace = true }
3939
serde = { workspace = true, features = ["derive"] }
4040
serde_json = { workspace = true }
41-
tokio = { workspace = true, features = ["rt-multi-thread", "io-std", "macros"] }
41+
tokio = { workspace = true, features = ["rt-multi-thread", "io-std", "macros"], optional = true }
4242
tower-lsp-server = { workspace = true, features = ["proposed"] }
4343

4444
[dev-dependencies]
4545
insta = { workspace = true }
4646
tokio = { workspace = true, features = ["rt-multi-thread", "io-std", "io-util", "macros"] }
4747

4848
[features]
49-
default = ["linter", "formatter"]
49+
default = ["linter", "formatter", "dep:tokio"]
50+
benchmark = ["linter", "formatter", "dep:tokio", "tokio/io-util"]
5051
linter = ["dep:oxc_linter"]
5152
formatter = ["dep:oxc_formatter"]

crates/oxc_language_server/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,19 @@ mod formatter;
99
#[cfg(feature = "linter")]
1010
mod linter;
1111
mod options;
12-
#[cfg(test)]
12+
#[cfg(any(test, feature = "benchmark"))]
1313
mod tests;
1414
mod tool;
1515
mod utils;
1616
mod worker;
1717

18-
use crate::backend::Backend;
18+
pub use crate::backend::Backend;
1919
#[cfg(feature = "formatter")]
2020
pub use crate::formatter::ServerFormatterBuilder;
2121
#[cfg(feature = "linter")]
2222
pub use crate::linter::ServerLinterBuilder;
23+
#[cfg(feature = "benchmark")]
24+
pub use crate::tests::*;
2325
pub use crate::tool::{Tool, ToolBuilder, ToolRestartChanges, ToolShutdownChanges};
2426

2527
pub type ConcurrentHashMap<K, V> = papaya::HashMap<K, V, FxBuildHasher>;

crates/oxc_language_server/src/tests.rs

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use serde_json::{Value, json};
44
use tokio::io::{AsyncReadExt, AsyncWriteExt, DuplexStream};
55
use tower_lsp_server::{
66
Client, LspService, Server,
7-
jsonrpc::{ErrorCode, Id, Request, Response},
7+
jsonrpc::{ErrorCode, Request, Response},
88
lsp_types::*,
99
};
1010

@@ -22,7 +22,7 @@ pub struct FakeTool;
2222

2323
pub const FAKE_COMMAND: &str = "fake.command";
2424

25-
const WORKSPACE: &str = "file:///path/to/workspace";
25+
pub const WORKSPACE: &str = "file:///path/to/workspace";
2626

2727
impl Tool for FakeTool {
2828
fn name(&self) -> &'static str {
@@ -152,14 +152,14 @@ impl Tool for FakeTool {
152152

153153
// A test server that can send requests and receive responses.
154154
// Copied from <https://github.com/veryl-lang/veryl/blob/888d83abaa58ca5a7ffef501a1c557e48c750b92/crates/languageserver/src/tests.rs>
155-
struct TestServer {
155+
pub struct TestServer {
156156
req_stream: DuplexStream,
157157
res_stream: DuplexStream,
158158
responses: VecDeque<String>,
159159
}
160160

161161
impl TestServer {
162-
fn new<F>(init: F) -> Self
162+
pub fn new<F>(init: F) -> Self
163163
where
164164
F: FnOnce(Client) -> Backend,
165165
{
@@ -208,26 +208,37 @@ impl TestServer {
208208
ret
209209
}
210210

211-
async fn send_request(&mut self, req: Request) {
211+
/// Sends a request to the server.
212+
///
213+
/// # Panics
214+
/// - If the stream cannot be written to.
215+
pub async fn send_request(&mut self, req: Request) {
212216
let req = serde_json::to_string(&req).unwrap();
213217
let req = Self::encode(&req);
214218
self.req_stream.write_all(req.as_bytes()).await.unwrap();
215219
}
216220

221+
#[cfg(test)]
217222
async fn send_response(&mut self, res: Response) {
218223
let res = serde_json::to_string(&res).unwrap();
219224
let res = Self::encode(&res);
220225
self.req_stream.write_all(res.as_bytes()).await.unwrap();
221226
}
222227

223-
async fn send_ack(&mut self, id: &Id) {
228+
#[cfg(test)]
229+
async fn send_ack(&mut self, id: &tower_lsp_server::jsonrpc::Id) {
224230
let req = Response::from_ok(id.clone(), None::<serde_json::Value>.into());
225231
let req = serde_json::to_string(&req).unwrap();
226232
let req = Self::encode(&req);
227233
self.req_stream.write_all(req.as_bytes()).await.unwrap();
228234
}
229235

230-
async fn recv_response(&mut self) -> Response {
236+
/// Receives a response from the server.
237+
///
238+
/// # Panics
239+
/// - If the stream cannot be read.
240+
/// - If the response cannot be deserialized.
241+
pub async fn recv_response(&mut self) -> Response {
231242
if self.responses.is_empty() {
232243
let mut buf = vec![0; 1024];
233244
let n = self.res_stream.read(&mut buf).await.unwrap();
@@ -240,6 +251,7 @@ impl TestServer {
240251
serde_json::from_str(&res).unwrap()
241252
}
242253

254+
#[cfg(test)]
243255
async fn recv_notification(&mut self) -> Request {
244256
if self.responses.is_empty() {
245257
let mut buf = vec![0; 1024];
@@ -255,6 +267,7 @@ impl TestServer {
255267

256268
/// Creates a new TestServer and performs the initialize and initialized sequence.
257269
/// The `init` closure is used to create the LanguageServer instance.
270+
#[cfg(test)]
258271
async fn new_initialized<F>(init: F, initialize: Request) -> Self
259272
where
260273
F: FnOnce(Client) -> Backend,
@@ -273,13 +286,15 @@ impl TestServer {
273286
server
274287
}
275288

289+
#[cfg(test)]
276290
async fn shutdown(&mut self, id: i64) {
277291
self.send_request(shutdown_request(id)).await;
278292
let shutdown_result = self.recv_response().await;
279293
assert!(shutdown_result.is_ok());
280-
assert_eq!(shutdown_result.id(), &Id::Number(id));
294+
assert_eq!(shutdown_result.id(), &tower_lsp_server::jsonrpc::Id::Number(id));
281295
}
282296

297+
#[cfg(test)]
283298
async fn shutdown_with_watchers(&mut self, id: i64) {
284299
// shutdown request
285300
self.send_request(shutdown_request(id)).await;
@@ -291,11 +306,15 @@ impl TestServer {
291306
let shutdown_result = self.recv_response().await;
292307

293308
assert!(shutdown_result.is_ok());
294-
assert_eq!(shutdown_result.id(), &Id::Number(id));
309+
assert_eq!(shutdown_result.id(), &tower_lsp_server::jsonrpc::Id::Number(id));
295310
}
296311
}
297312

298-
fn initialize_request(
313+
/// Creates an initialize request with the given parameters.
314+
///
315+
/// # Panics
316+
/// - If the workspace URI is not a valid URI.
317+
pub fn initialize_request(
299318
workspace_configuration: bool,
300319
dynamic_watchers: bool,
301320
workspace_edit: bool,
@@ -327,16 +346,17 @@ fn initialize_request(
327346
Request::build("initialize").params(json!(params)).id(1).finish()
328347
}
329348

330-
fn initialized_notification() -> Request {
349+
pub fn initialized_notification() -> Request {
331350
let params = InitializedParams {};
332351

333352
Request::build("initialized").params(json!(params)).finish()
334353
}
335354

336-
fn shutdown_request(id: i64) -> Request {
355+
pub fn shutdown_request(id: i64) -> Request {
337356
Request::build("shutdown").id(id).finish()
338357
}
339358

359+
#[cfg(test)]
340360
fn execute_command_request(command: &str, arguments: &[serde_json::Value], id: i64) -> Request {
341361
Request::build("workspace/executeCommand")
342362
.id(id)
@@ -347,6 +367,7 @@ fn execute_command_request(command: &str, arguments: &[serde_json::Value], id: i
347367
.finish()
348368
}
349369

370+
#[cfg(test)]
350371
fn workspace_folders_changed(
351372
added: Vec<WorkspaceFolder>,
352373
removed: Vec<WorkspaceFolder>,
@@ -357,6 +378,7 @@ fn workspace_folders_changed(
357378
Request::build("workspace/didChangeWorkspaceFolders").params(json!(params)).finish()
358379
}
359380

381+
#[cfg(test)]
360382
async fn acknowledge_registrations(server: &mut TestServer) {
361383
// client/registerCapability request
362384
let register_request = server.recv_notification().await;
@@ -366,6 +388,7 @@ async fn acknowledge_registrations(server: &mut TestServer) {
366388
server.send_ack(register_request.id().unwrap()).await;
367389
}
368390

391+
#[cfg(test)]
369392
async fn acknowledge_unregistrations(server: &mut TestServer) {
370393
// client/unregisterCapability request
371394
let unregister_request = server.recv_notification().await;
@@ -375,6 +398,7 @@ async fn acknowledge_unregistrations(server: &mut TestServer) {
375398
server.send_ack(unregister_request.id().unwrap()).await;
376399
}
377400

401+
#[cfg(test)]
378402
async fn response_to_configuration(
379403
server: &mut TestServer,
380404
configurations: Vec<serde_json::Value>,
@@ -389,6 +413,7 @@ async fn response_to_configuration(
389413
.await;
390414
}
391415

416+
#[cfg(test)]
392417
fn did_change_watched_files(uri: &str) -> Request {
393418
Request::build("workspace/didChangeWatchedFiles")
394419
.params(json!({
@@ -402,13 +427,18 @@ fn did_change_watched_files(uri: &str) -> Request {
402427
.finish()
403428
}
404429

430+
#[cfg(test)]
405431
fn did_change_configuration(new_config: Option<serde_json::Value>) -> Request {
406432
Request::build("workspace/didChangeConfiguration")
407433
.params(json!(DidChangeConfigurationParams { settings: new_config.unwrap_or_default() }))
408434
.finish()
409435
}
410436

411-
fn did_open(uri: &str, text: &str) -> Request {
437+
/// Creates a didOpen notification for the given URI and text.
438+
///
439+
/// # Panics
440+
/// - If the URI is not a valid URI.
441+
pub fn did_open(uri: &str, text: &str) -> Request {
412442
let params = DidOpenTextDocumentParams {
413443
text_document: TextDocumentItem {
414444
uri: uri.parse().unwrap(),
@@ -421,6 +451,7 @@ fn did_open(uri: &str, text: &str) -> Request {
421451
Request::build("textDocument/didOpen").params(json!(params)).finish()
422452
}
423453

454+
#[cfg(test)]
424455
fn did_change(uri: &str, text: &str) -> Request {
425456
let params = DidChangeTextDocumentParams {
426457
text_document: VersionedTextDocumentIdentifier { uri: uri.parse().unwrap(), version: 2 },
@@ -434,6 +465,7 @@ fn did_change(uri: &str, text: &str) -> Request {
434465
Request::build("textDocument/didChange").params(json!(params)).finish()
435466
}
436467

468+
#[cfg(test)]
437469
fn did_save(uri: &str, text: &str) -> Request {
438470
let params = DidSaveTextDocumentParams {
439471
text_document: TextDocumentIdentifier { uri: uri.parse().unwrap() },
@@ -443,6 +475,7 @@ fn did_save(uri: &str, text: &str) -> Request {
443475
Request::build("textDocument/didSave").params(json!(params)).finish()
444476
}
445477

478+
#[cfg(test)]
446479
fn did_close(uri: &str) -> Request {
447480
let params = DidCloseTextDocumentParams {
448481
text_document: TextDocumentIdentifier { uri: uri.parse().unwrap() },
@@ -451,6 +484,7 @@ fn did_close(uri: &str) -> Request {
451484
Request::build("textDocument/didClose").params(json!(params)).finish()
452485
}
453486

487+
#[cfg(test)]
454488
fn code_action(id: i64, uri: &str) -> Request {
455489
let params = CodeActionParams {
456490
text_document: TextDocumentIdentifier { uri: uri.parse().unwrap() },
@@ -463,23 +497,29 @@ fn code_action(id: i64, uri: &str) -> Request {
463497
Request::build("textDocument/codeAction").id(id).params(json!(params)).finish()
464498
}
465499

500+
#[cfg(test)]
466501
fn test_configuration_request(id: i64) -> Request {
467502
Request::build("test/configuration").id(id).params(json!(null)).finish()
468503
}
469504

505+
pub fn server_info() -> ServerInfo {
506+
ServerInfo { name: "oxc".to_owned(), version: Some("1.0.0".to_owned()) }
507+
}
508+
470509
#[cfg(test)]
471510
mod test_suite {
472511
use serde_json::{Value, json};
473512
use tower_lsp_server::{
474513
jsonrpc::{Id, Response},
475514
lsp_types::{
476-
ApplyWorkspaceEditResponse, InitializeResult, PublishDiagnosticsParams, ServerInfo,
477-
WorkspaceEdit, WorkspaceFolder,
515+
ApplyWorkspaceEditResponse, InitializeResult, PublishDiagnosticsParams, WorkspaceEdit,
516+
WorkspaceFolder,
478517
},
479518
};
480519

481520
use crate::{
482521
backend::Backend,
522+
server_info,
483523
tests::{
484524
FAKE_COMMAND, FakeToolBuilder, TestServer, WORKSPACE, acknowledge_registrations,
485525
acknowledge_unregistrations, code_action, did_change, did_change_configuration,
@@ -489,10 +529,6 @@ mod test_suite {
489529
},
490530
};
491531

492-
fn server_info() -> ServerInfo {
493-
ServerInfo { name: "oxc".to_owned(), version: Some("1.0.0".to_owned()) }
494-
}
495-
496532
#[tokio::test]
497533
async fn test_basic_start_and_shutdown_flow() {
498534
let mut server = TestServer::new(|client| Backend::new(client, server_info(), vec![]));

0 commit comments

Comments
 (0)