2020-05-06 02:23:41 +03:00
|
|
|
//! 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::*;
|
2020-05-12 16:20:29 +03:00
|
|
|
use enso_protocol::types::*;
|
2020-05-06 02:23:41 +03:00
|
|
|
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";
|
|
|
|
|
2020-05-12 16:20:29 +03:00
|
|
|
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
|
|
|
|
"#;
|
2020-05-06 02:23:41 +03:00
|
|
|
|
|
|
|
wasm_bindgen_test_configure!(run_in_browser);
|
|
|
|
|
|
|
|
//#[wasm_bindgen_test::wasm_bindgen_test(async)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
async fn file_operations() {
|
2020-05-24 19:19:57 +03:00
|
|
|
let ws = WebSocket::new_opened(default(),SERVER_ENDPOINT).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
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());
|
|
|
|
|
2020-05-12 16:20:29 +03:00
|
|
|
let client_id = uuid::Uuid::new_v4();
|
2020-05-15 14:49:05 +03:00
|
|
|
let session = client.init_protocol_connection(&client_id).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
let session = session.expect("Couldn't initialize session.");
|
|
|
|
let root_id = session.content_roots[0];
|
|
|
|
|
2020-05-12 16:20:29 +03:00
|
|
|
let file = Path{root_id,segments:vec!["src".into(),"Main.enso".into()]};
|
|
|
|
let contents = MAIN_CODE.to_string();
|
2020-05-15 14:49:05 +03:00
|
|
|
let result = client.write_file(&file,&contents).await;
|
2020-05-12 16:20:29 +03:00
|
|
|
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();
|
2020-05-15 14:49:05 +03:00
|
|
|
let response = client.write_file(&visualisation_file,&contents).await;
|
2020-05-12 16:20:29 +03:00
|
|
|
response.expect("Couldn't write visualisation file.");
|
|
|
|
|
|
|
|
let package_file = Path{root_id,segments:vec!["package.yaml".into()]};
|
|
|
|
let contents = PACKAGE_YAML.to_string();
|
2020-05-15 14:49:05 +03:00
|
|
|
let response = client.write_file(&package_file,&contents).await;
|
2020-05-12 16:20:29 +03:00
|
|
|
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.");
|
2020-05-25 13:00:27 +03:00
|
|
|
let execution_context_id = execution_context.context_id;
|
2020-05-12 16:20:29 +03:00
|
|
|
|
|
|
|
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);
|
2020-05-15 14:49:05 +03:00
|
|
|
let response = client.push_to_execution_context(&execution_context_id,&stack_item).await;
|
2020-05-12 16:20:29 +03:00
|
|
|
response.expect("Couldn't push execution context.");
|
|
|
|
|
2020-05-15 14:49:05 +03:00
|
|
|
let response = client.pop_from_execution_context(&execution_context_id).await;
|
2020-05-12 16:20:29 +03:00
|
|
|
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
|
2020-05-15 14:49:05 +03:00
|
|
|
(&visualisation_id,&expression_id,&visualisation_config);
|
2020-05-12 16:20:29 +03:00
|
|
|
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};
|
2020-05-15 14:49:05 +03:00
|
|
|
let response = client.modify_visualisation(&visualisation_id,&visualisation_config).await;
|
2020-05-12 16:20:29 +03:00
|
|
|
response.expect("Couldn't modify visualisation.");
|
|
|
|
|
|
|
|
let response = client.detach_visualisation
|
2020-05-15 14:49:05 +03:00
|
|
|
(&execution_context_id,&visualisation_id,&expression_id).await;
|
2020-05-12 16:20:29 +03:00
|
|
|
response.expect("Couldn't detach visualisation.");
|
|
|
|
|
2020-05-15 14:49:05 +03:00
|
|
|
let response = client.destroy_execution_context(&execution_context_id).await;
|
2020-05-12 16:20:29 +03:00
|
|
|
response.expect("Couldn't destroy execution context.");
|
|
|
|
|
2020-05-06 02:23:41 +03:00
|
|
|
let path = Path{root_id, segments:vec!["foo".into()]};
|
|
|
|
let name = "text.txt".into();
|
|
|
|
let object = FileSystemObject::File {name,path};
|
2020-05-15 14:49:05 +03:00
|
|
|
client.create_file(&object).await.expect("Couldn't create file.");
|
2020-05-06 02:23:41 +03:00
|
|
|
|
|
|
|
let file_path = Path{root_id, segments:vec!["foo".into(),"text.txt".into()]};
|
|
|
|
let contents = "Hello world!".to_string();
|
2020-05-15 14:49:05 +03:00
|
|
|
let result = client.write_file(&file_path,&contents).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
result.expect("Couldn't write file.");
|
|
|
|
|
2020-05-15 14:49:05 +03:00
|
|
|
let response = client.file_info(&file_path).await.expect("Couldn't get status.");
|
2020-05-06 02:23:41 +03:00
|
|
|
assert_eq!(response.attributes.byte_size,12);
|
|
|
|
assert_eq!(response.attributes.kind,object);
|
|
|
|
|
2020-05-15 14:49:05 +03:00
|
|
|
let response = client.file_list(&Path{root_id,segments:vec!["foo".into()]}).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
let response = response.expect("Couldn't get file list");
|
|
|
|
assert!(response.paths.iter().any(|file_system_object| object == *file_system_object));
|
|
|
|
|
2020-05-15 14:49:05 +03:00
|
|
|
let read = client.read_file(&file_path).await.expect("Couldn't read contents.");
|
2020-05-06 02:23:41 +03:00
|
|
|
assert_eq!(contents,read.contents);
|
|
|
|
|
|
|
|
let new_path = Path{root_id,segments:vec!["foo".into(),"new_text.txt".into()]};
|
2020-05-15 14:49:05 +03:00
|
|
|
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.");
|
2020-05-06 02:23:41 +03:00
|
|
|
assert_eq!(contents,read.contents);
|
|
|
|
|
|
|
|
let move_path = Path{root_id,segments:vec!["foo".into(),"moved_text.txt".into()]};
|
2020-05-15 14:49:05 +03:00
|
|
|
let file = client.file_exists(&move_path).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
let file = file.expect("Couldn't check if file exists.");
|
|
|
|
if file.exists {
|
2020-05-15 14:49:05 +03:00
|
|
|
client.delete_file(&move_path).await.expect("Couldn't delete file");
|
|
|
|
let file = client.file_exists(&move_path).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
let file = file.expect("Couldn't check if file exists.");
|
|
|
|
assert_eq!(file.exists,false);
|
|
|
|
}
|
|
|
|
|
2020-05-15 14:49:05 +03:00
|
|
|
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");
|
2020-05-06 02:23:41 +03:00
|
|
|
assert_eq!(contents,read.contents);
|
2020-05-12 16:20:29 +03:00
|
|
|
|
2020-05-25 13:00:27 +03:00
|
|
|
let register_options = RegisterOptions::Path{path:move_path.clone()};
|
2020-05-12 16:20:29 +03:00
|
|
|
let method = "text/canEdit".to_string();
|
|
|
|
let capability_registration = CapabilityRegistration {method,register_options};
|
2020-05-15 14:49:05 +03:00
|
|
|
let response = client.open_text_file(&move_path).await;
|
2020-05-12 16:20:29 +03:00
|
|
|
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()};
|
2020-05-15 14:49:05 +03:00
|
|
|
client.apply_text_file_edit(&edit).await.expect("Couldn't apply edit.");
|
2020-05-12 16:20:29 +03:00
|
|
|
|
2020-05-15 14:49:05 +03:00
|
|
|
let future = client.save_text_file(&move_path,&new_version).await;
|
2020-05-12 16:20:29 +03:00
|
|
|
future.expect("Couldn't save file.");
|
|
|
|
|
2020-05-15 14:49:05 +03:00
|
|
|
client.close_text_file(&move_path).await.expect("Couldn't close text file.");
|
2020-05-12 16:20:29 +03:00
|
|
|
|
2020-05-15 14:49:05 +03:00
|
|
|
let read = client.read_file(&move_path).await.expect("Couldn't read contents.");
|
2020-05-12 16:20:29 +03:00
|
|
|
assert_eq!("Hello, world!".to_string(),read.contents);
|
2020-05-06 02:23:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//#[wasm_bindgen_test::wasm_bindgen_test(async)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
async fn file_events() {
|
2020-05-24 19:19:57 +03:00
|
|
|
let ws = WebSocket::new_opened(default(),SERVER_ENDPOINT).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
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();
|
2020-05-15 14:49:05 +03:00
|
|
|
let session = client.init_protocol_connection(&client_id).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
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()]};
|
2020-05-15 14:49:05 +03:00
|
|
|
let file = client.file_exists(&path).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
let file = file.expect("Couldn't check if file exists.");
|
|
|
|
if file.exists {
|
2020-05-15 14:49:05 +03:00
|
|
|
client.delete_file(&path).await.expect("Couldn't delete file");
|
|
|
|
let file = client.file_exists(&path).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
let file = file.expect("Couldn't check if file exists.");
|
|
|
|
assert_eq!(file.exists,false);
|
|
|
|
}
|
|
|
|
|
|
|
|
let path = Path{root_id, segments:vec![]};
|
2020-05-25 13:00:27 +03:00
|
|
|
let options = RegisterOptions::Path{path};
|
2020-05-15 14:49:05 +03:00
|
|
|
let capability = client.acquire_capability(&"receivesTreeUpdates".to_string(),&options).await;
|
2020-05-06 02:23:41 +03:00
|
|
|
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()};
|
2020-05-15 14:49:05 +03:00
|
|
|
client.create_file(&object).await.expect("Couldn't create file.");
|
2020-05-06 02:23:41 +03:00
|
|
|
|
|
|
|
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.");
|
|
|
|
}
|
|
|
|
}
|
2020-05-24 19:19:57 +03:00
|
|
|
|
|
|
|
//#[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.
|
|
|
|
}
|