From 7e5d118249290fae8dcdfe56bf0f8796c1c73834 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 13 Apr 2023 10:46:06 +0100 Subject: [PATCH] initial testing of fsm --- Cargo.lock | 2 + crates/analyzer-host/Cargo.toml | 4 ++ crates/analyzer-host/src/fsm.rs | 6 +- crates/analyzer-host/src/lib.rs | 4 ++ crates/analyzer-host/src/tests.rs | 1 + crates/analyzer-host/src/tests/unit_tests.rs | 70 ++++++++++++++++++++ 6 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 crates/analyzer-host/src/tests.rs create mode 100644 crates/analyzer-host/src/tests/unit_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 53c41dd..c3b32f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,7 @@ dependencies = [ "analyzer-abstractions", "analyzer-core", "async-channel", + "async-io", "async-rwlock", "cancellation", "dyn-clonable", @@ -71,6 +72,7 @@ dependencies = [ "itertools", "serde", "serde_json", + "tester", "thiserror", "tracing-subscriber", ] diff --git a/crates/analyzer-host/Cargo.toml b/crates/analyzer-host/Cargo.toml index ddb7cd0..3518971 100644 --- a/crates/analyzer-host/Cargo.toml +++ b/crates/analyzer-host/Cargo.toml @@ -17,3 +17,7 @@ serde = "1.0.143" serde_json = "1.0.83" thiserror = "1.0.37" tracing-subscriber = "0.3.16" + +[dev-dependencies] +async-io = "1.13.0" +tester = { path = "../tester" } diff --git a/crates/analyzer-host/src/fsm.rs b/crates/analyzer-host/src/fsm.rs index aeb879d..f80a2b9 100644 --- a/crates/analyzer-host/src/fsm.rs +++ b/crates/analyzer-host/src/fsm.rs @@ -1,12 +1,13 @@ use analyzer_abstractions::{fs::AnyEnumerableFileSystem, tracing::info}; use async_rwlock::RwLock as AsyncRwLock; +use serde_json::Value; use std::{ collections::HashMap, sync::{Arc, RwLock}, }; use crate::{ - json_rpc::{message::Message, ErrorCode}, + json_rpc::{message::{Message, Request, self, Notification}, ErrorCode}, lsp::{dispatch::Dispatch, request::RequestManager, state::LspServerState, DispatchBuilder, LspProtocolError}, lsp_impl::{ active_initialized::create_dispatcher as create_dispatcher_active_initialized, @@ -52,6 +53,9 @@ impl LspProtocolMachine { /// Returns `true` if the current [`LspProtocolMachine`] is in an active state; otherwise `false`. pub fn is_active(&self) -> bool { self.current_state != LspServerState::Stopped } + /// Returns the current_state that the [`LspProtocolMachine`] is in. + pub fn current_state(&self) -> LspServerState { self.current_state } + /// Processes a [`Message`] for the current [`LspProtocolMachine`], and returns an optional [`Message`] that represents /// its response. pub async fn process_message(&mut self, message: Arc) -> Result, LspProtocolError> { diff --git a/crates/analyzer-host/src/lib.rs b/crates/analyzer-host/src/lib.rs index 3e30c53..0e45b30 100644 --- a/crates/analyzer-host/src/lib.rs +++ b/crates/analyzer-host/src/lib.rs @@ -161,3 +161,7 @@ impl AnalyzerHost { Ok(()) } } + +// Unit test fixtures. +#[cfg(test)] +mod tests; diff --git a/crates/analyzer-host/src/tests.rs b/crates/analyzer-host/src/tests.rs new file mode 100644 index 0000000..5764abb --- /dev/null +++ b/crates/analyzer-host/src/tests.rs @@ -0,0 +1 @@ +pub(crate) mod unit_tests; diff --git a/crates/analyzer-host/src/tests/unit_tests.rs b/crates/analyzer-host/src/tests/unit_tests.rs new file mode 100644 index 0000000..91e3fbc --- /dev/null +++ b/crates/analyzer-host/src/tests/unit_tests.rs @@ -0,0 +1,70 @@ +use std::sync::Arc; + +use analyzer_abstractions::lsp_types::{ + Position, TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkDoneProgressParams, +}; +use serde_json::Value; + +use crate::{ + fs::LspEnumerableFileSystem, + fsm::LspProtocolMachine, + json_rpc::message::{Message, Notification, Request}, + lsp::{request::RequestManager, state::LspServerState}, +}; + +#[test] +fn test_lsp_states() { + let rm = RequestManager::new(async_channel::unbounded::()); + let mut lsp = + LspProtocolMachine::new(None, rm.clone(), Arc::new(Box::new(LspEnumerableFileSystem::new(rm.clone())))); + assert_eq!(lsp.current_state(), LspServerState::ActiveUninitialized); + assert_eq!(lsp.is_active(), true); + + let mut params = serde_json::json!(analyzer_abstractions::lsp_types::InitializeParams { ..Default::default() }); + let mut message = Message::Request(Request { id: 0.into(), method: String::from("initialize"), params: params }); + let mut output = async_io::block_on(lsp.process_message(Arc::new(message))); + assert!(output.is_ok()); + assert_eq!(lsp.current_state(), LspServerState::Initializing); + assert_eq!(lsp.is_active(), true); + + let url = Url::parse("https://example.net").unwrap(); + let hover_params = analyzer_abstractions::lsp_types::HoverParams { + text_document_position_params: TextDocumentPositionParams { + text_document: TextDocumentIdentifier { uri: url }, + position: Position { line: 0, character: 0 }, + }, + work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, + }; + params = serde_json::json!(hover_params); + message = Message::Request(Request { id: 0.into(), method: String::from("textDocument/hover"), params: params }); + output = async_io::block_on(lsp.process_message(Arc::new(message))); + assert!(output.is_ok()); + assert_eq!(lsp.current_state(), LspServerState::Initializing); + assert_eq!(lsp.is_active(), true); + + params = serde_json::json!(analyzer_abstractions::lsp_types::InitializedParams {}); + message = Message::Notification(Notification { method: String::from("initialized"), params: params }); + output = async_io::block_on(lsp.process_message(Arc::new(message))); + assert!(output.is_ok()); + assert_eq!(lsp.current_state(), LspServerState::ActiveInitialized); + assert_eq!(lsp.is_active(), true); + + params = serde_json::json!(hover_params); + message = Message::Notification(Notification { method: String::from("textDocument/hover"), params: params }); + output = async_io::block_on(lsp.process_message(Arc::new(message))); + assert!(output.is_err()); + assert_eq!(lsp.current_state(), LspServerState::ActiveInitialized); + assert_eq!(lsp.is_active(), true); + + message = Message::Request(Request { id: 0.into(), method: String::from("shutdown"), params: Value::Null }); + output = async_io::block_on(lsp.process_message(Arc::new(message))); + assert!(output.is_ok()); + assert_eq!(lsp.current_state(), LspServerState::ShuttingDown); + assert_eq!(lsp.is_active(), true); + + message = Message::Notification(Notification { method: String::from("exit"), params: Value::Null }); + output = async_io::block_on(lsp.process_message(Arc::new(message))); + assert!(output.is_ok()); + assert_eq!(lsp.current_state(), LspServerState::Stopped); + assert_eq!(lsp.is_active(), false); +}