enso/gui/src/rust/ide/tests/language_server.rs

276 lines
12 KiB
Rust
Raw Normal View History

//! Language Server integration tests.
//!
//! They are disabled by default, as there is no CI infrastructure to run them with Lanaguage
//! Server. To run tests manually, uncomment the `#[wasm_bindgen_test::wasm_bindgen_test(async)]`
//! attributes and use wasm-bindgen test.
//!
//! Note that running Lanugage Server is expected at `SERVER_ENDPOINT` (by default localhost:30616).
//! To run the language server manually run in the `enso` repository e.g.
//! ```
//! sbt "runner/run --server --root-id 6f7d58dd-8ee8-44cf-9ab7-9f0454033641 --path $HOME/ensotmp --rpc-port 30616"
//! ```
use ide::prelude::*;
use enso_protocol::language_server::*;
use enso_protocol::types::*;
use ide::transport::web::WebSocket;
use wasm_bindgen_test::wasm_bindgen_test_configure;
/// The endpoint at which the Language Server should be accepting WS connections.
const SERVER_ENDPOINT:&str = "ws://localhost:30616";
const PACKAGE_YAML:&str = r#"
maintainer: ''
license: ''
name: Test
version: ''
author: ''
"#;
const MAIN_CODE:&str = r#"
main =
x = 6
y = x.foo 5
z = y + 5
z
Number.foo = x ->
y = this + 3
z = y * x
z
#### METADATA ####
[[{"index": {"value": 98}, "size": {"value": 5}}, "5fc0c11d-bd83-4ca3-b847-b8e362f7658c"],[{"index": {"value": 81}, "size": {"value": 8}}, "1cda3676-bd62-41f8-b6a1-a1e1b7c73d18"],[{"index": {"value": 42}, "size": {"value": 5}}, "899a11e5-4d2b-43dc-a867-2f2ef2d2ba62"],[{"index": {"value": 26}, "size": {"value": 7}}, "37f284d4-c593-4e65-a4be-4948fbd2adfb"],[{"index": {"value": 16}, "size": {"value": 1}}, "c553533e-a2b9-4305-9f12-b8fe7781f933"]]
[]"#;
const VISUALISATION_CODE:&str = r#"
encode = x -> x.to_text
incAndEncode = x -> here.encode x+1
"#;
wasm_bindgen_test_configure!(run_in_browser);
//#[wasm_bindgen_test::wasm_bindgen_test(async)]
#[allow(dead_code)]
async fn file_operations() {
let ws = WebSocket::new_opened(default(),SERVER_ENDPOINT).await;
let ws = ws.expect("Couldn't connect to WebSocket server.");
let client = Client::new(ws);
let _executor = ide::setup_global_executor();
executor::global::spawn(client.runner());
let client_id = uuid::Uuid::new_v4();
let session = client.init_protocol_connection(&client_id).await;
let session = session.expect("Couldn't initialize session.");
let root_id = session.content_roots[0];
let file = Path{root_id,segments:vec!["src".into(),"Main.enso".into()]};
let contents = MAIN_CODE.to_string();
let result = client.write_file(&file,&contents).await;
result.expect("Couldn't write main code file.");
let visualisation_file = Path{root_id,segments:vec!["src".into(),"Visualisation.enso".into()]};
let contents = VISUALISATION_CODE.to_string();
let response = client.write_file(&visualisation_file,&contents).await;
response.expect("Couldn't write visualisation file.");
let package_file = Path{root_id,segments:vec!["package.yaml".into()]};
let contents = PACKAGE_YAML.to_string();
let response = client.write_file(&package_file,&contents).await;
response.expect("Couldn't write yaml file.");
let execution_context = client.create_execution_context().await;
let execution_context = execution_context.expect("Couldn't create execution context.");
let execution_context_id = execution_context.context_id;
let defined_on_type = "Main".to_string();
let name = "main".to_string();
let method_pointer = MethodPointer{file,defined_on_type,name};
let positional_arguments_expressions = default();
let this_argument_expression = default();
let explicit_call = ExplicitCall
{method_pointer,positional_arguments_expressions,this_argument_expression};
let stack_item = StackItem::ExplicitCall(explicit_call);
let response = client.push_to_execution_context(&execution_context_id,&stack_item).await;
response.expect("Couldn't push execution context.");
let response = client.pop_from_execution_context(&execution_context_id).await;
response.expect("Couldn't pop execution context.");
let visualisation_id = uuid::Uuid::new_v4();
let expression_id = uuid::Uuid::parse_str("c553533e-a2b9-4305-9f12-b8fe7781f933");
let expression_id = expression_id.expect("Couldn't parse expression id.");
let expression = "x -> here.encode x".to_string();
let visualisation_module = "Test.Visualisation".to_string();
let visualisation_config = VisualisationConfiguration
{execution_context_id,expression,visualisation_module};
let response = client.attach_visualisation
(&visualisation_id,&expression_id,&visualisation_config);
response.await.expect("Couldn't attach visualisation.");
let expression = "x -> here.incAndEncode".to_string();
let visualisation_module = "Test.Visualisation".to_string();
let visualisation_config = VisualisationConfiguration
{execution_context_id,expression,visualisation_module};
let response = client.modify_visualisation(&visualisation_id,&visualisation_config).await;
response.expect("Couldn't modify visualisation.");
let response = client.detach_visualisation
(&execution_context_id,&visualisation_id,&expression_id).await;
response.expect("Couldn't detach visualisation.");
let response = client.destroy_execution_context(&execution_context_id).await;
response.expect("Couldn't destroy execution context.");
let path = Path{root_id, segments:vec!["foo".into()]};
let name = "text.txt".into();
let object = FileSystemObject::File {name,path};
client.create_file(&object).await.expect("Couldn't create file.");
let file_path = Path{root_id, segments:vec!["foo".into(),"text.txt".into()]};
let contents = "Hello world!".to_string();
let result = client.write_file(&file_path,&contents).await;
result.expect("Couldn't write file.");
let response = client.file_info(&file_path).await.expect("Couldn't get status.");
assert_eq!(response.attributes.byte_size,12);
assert_eq!(response.attributes.kind,object);
let response = client.file_list(&Path{root_id,segments:vec!["foo".into()]}).await;
let response = response.expect("Couldn't get file list");
assert!(response.paths.iter().any(|file_system_object| object == *file_system_object));
let read = client.read_file(&file_path).await.expect("Couldn't read contents.");
assert_eq!(contents,read.contents);
let new_path = Path{root_id,segments:vec!["foo".into(),"new_text.txt".into()]};
client.copy_file(&file_path,&new_path).await.expect("Couldn't copy file");
let read = client.read_file(&new_path).await.expect("Couldn't read contents.");
assert_eq!(contents,read.contents);
let move_path = Path{root_id,segments:vec!["foo".into(),"moved_text.txt".into()]};
let file = client.file_exists(&move_path).await;
let file = file.expect("Couldn't check if file exists.");
if file.exists {
client.delete_file(&move_path).await.expect("Couldn't delete file");
let file = client.file_exists(&move_path).await;
let file = file.expect("Couldn't check if file exists.");
assert_eq!(file.exists,false);
}
client.move_file(&new_path,&move_path).await.expect("Couldn't move file");
let read = client.read_file(&move_path).await.expect("Couldn't read contents");
assert_eq!(contents,read.contents);
let register_options = RegisterOptions::Path{path:move_path.clone()};
let method = "text/canEdit".to_string();
let capability_registration = CapabilityRegistration {method,register_options};
let response = client.open_text_file(&move_path).await;
let response = response.expect("Couldn't open text file.");
assert_eq!(response.content, "Hello world!");
assert_eq!(response.write_capability, Some(capability_registration));
let start = Position{line:0,character:5};
let end = Position{line:0,character:5};
let range = TextRange{start,end};
let text = ",".to_string();
let text_edit = TextEdit{range,text};
let edits = vec![text_edit];
let old_version = Sha3_224::new(b"Hello world!");
let new_version = Sha3_224::new(b"Hello, world!");
let path = move_path.clone();
let edit = FileEdit {path,edits,old_version,new_version:new_version.clone()};
client.apply_text_file_edit(&edit).await.expect("Couldn't apply edit.");
let future = client.save_text_file(&move_path,&new_version).await;
future.expect("Couldn't save file.");
client.close_text_file(&move_path).await.expect("Couldn't close text file.");
let read = client.read_file(&move_path).await.expect("Couldn't read contents.");
assert_eq!("Hello, world!".to_string(),read.contents);
}
//#[wasm_bindgen_test::wasm_bindgen_test(async)]
#[allow(dead_code)]
async fn file_events() {
let ws = WebSocket::new_opened(default(),SERVER_ENDPOINT).await;
let ws = ws.expect("Couldn't connect to WebSocket server.");
let client = Client::new(ws);
let mut stream = client.events();
let _executor = ide::setup_global_executor();
executor::global::spawn(client.runner());
let client_id = uuid::Uuid::default();
let session = client.init_protocol_connection(&client_id).await;
let session = session.expect("Couldn't initialize session.");
let root_id = session.content_roots[0];
let path = Path{root_id,segments:vec!["test.txt".into()]};
let file = client.file_exists(&path).await;
let file = file.expect("Couldn't check if file exists.");
if file.exists {
client.delete_file(&path).await.expect("Couldn't delete file");
let file = client.file_exists(&path).await;
let file = file.expect("Couldn't check if file exists.");
assert_eq!(file.exists,false);
}
let path = Path{root_id, segments:vec![]};
let options = RegisterOptions::Path{path};
let capability = client.acquire_capability(&"receivesTreeUpdates".to_string(),&options).await;
capability.expect("Couldn't acquire receivesTreeUpdates capability.");
let path = Path{root_id, segments:vec![]};
let name = "test.txt".into();
let object = FileSystemObject::File {name,path:path.clone()};
client.create_file(&object).await.expect("Couldn't create file.");
let path = Path{root_id,segments:vec!["test.txt".into()]};
let kind = FileEventKind::Added;
let event = FileEvent {path,kind};
let notification = Notification::FileEvent {event};
let event = stream.next().await.expect("Couldn't get any notification.");
if let Event::Notification(incoming_notification) = event {
assert_eq!(incoming_notification,notification);
} else {
panic!("Incoming event isn't a notification.");
}
}
//#[wasm_bindgen_test::wasm_bindgen_test(async)]
#[allow(dead_code)]
/// This integration test covers:
/// * using project picker to open (or create) a project
/// * establishing a binary protocol connection with Language Server
/// * writing and reading a file using the binary protocol
async fn binary_protocol_test() {
// Setup project
let _guard = ide::setup_global_executor();
let logger = Logger::new("Test");
let endpoint = ide::PROJECT_MANAGER_ENDPOINT;
let ws = WebSocket::new_opened(logger.clone_ref(),endpoint).await.unwrap();
let pm = ide::setup_project_manager(ws);
let project = ide::open_most_recent_project_or_create_new(&logger,&pm).await.unwrap();
println!("Got project: {:?}", project);
let path = Path::new(project.language_server_rpc.content_root(), &["test_file.txt"]);
let contents = "Hello!".as_bytes();
let written = project.language_server_bin.write_file(&path,contents).await.unwrap();
println!("Written: {:?}", written);
let read_back = project.language_server_bin.read_file(&path).await.unwrap();
println!("Read back: {:?}", read_back);
assert_eq!(contents, read_back.as_slice());
// TODO [mwu]
// In future it would be nice to have here also a test for receiving a visualization update.
}