Skip to content

Commit

Permalink
LS: Add first tests for the new project model
Browse files Browse the repository at this point in the history
commit-id:7caaac0c
  • Loading branch information
mkaput committed Jun 25, 2024
1 parent e6cf94b commit 22e2c8a
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 7 deletions.
25 changes: 20 additions & 5 deletions crates/cairo-lang-language-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ use cairo_lang_utils::{Intern, LookupIntern, Upcast};
use serde_json::Value;
use tokio::task::spawn_blocking;
use tower_lsp::jsonrpc::{Error as LSPError, Result as LSPResult};
use tower_lsp::lsp_types::request::Request;
use tower_lsp::lsp_types::*;
use tower_lsp::{Client, ClientSocket, LanguageServer, LspService, Server};
use tracing::{debug, error, info, trace_span, warn, Instrument};
use tracing::{debug, error, info, trace, trace_span, warn, Instrument};

use crate::config::Config;
use crate::ide::semantic_highlighting::SemanticTokenKind;
Expand All @@ -97,7 +98,7 @@ mod config;
mod env_config;
mod ide;
mod lang;
mod lsp;
pub mod lsp;
mod markdown;
mod project;
mod server;
Expand Down Expand Up @@ -287,9 +288,14 @@ struct Backend {

impl Backend {
fn build_service(tricks: Tricks) -> (LspService<Self>, ClientSocket) {
LspService::build(|client| Self::new(client, tricks))
.custom_method("vfs/provide", Self::vfs_provide)
.finish()
let service = LspService::build(|client| Self::new(client, tricks))
.custom_method("vfs/provide", Self::vfs_provide);

#[cfg(feature = "testing")]
let service = service
.custom_method(lsp::methods::test::DebugProjects::METHOD, Self::test_debug_projects);

service.finish()
}

fn new(client: Client, tricks: Tricks) -> Self {
Expand Down Expand Up @@ -594,6 +600,15 @@ impl Backend {
}
}

#[cfg(feature = "testing")]
impl Backend {
async fn test_debug_projects(&self) -> LSPResult<String> {
let projects = self.projects.lock().await;
trace!("debugging projects: {projects:?}");
Ok(format!("{projects:#?}"))
}
}

enum ServerCommands {
Reload,
}
Expand Down
16 changes: 16 additions & 0 deletions crates/cairo-lang-language-server/src/lsp/methods.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! This module contains definitions of custom methods (aka requests or notifications) for the
//! Language Server Protocol that are provided by the Cairo Language Server.

use tower_lsp::lsp_types::request::Request;

#[cfg(feature = "testing")]
pub mod test {
use super::*;

pub struct DebugProjects;
impl Request for DebugProjects {
type Params = ();
type Result = String;
const METHOD: &'static str = "test/debugProjects";
}
}
3 changes: 2 additions & 1 deletion crates/cairo-lang-language-server/src/lsp/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod client_capabilities;
pub(crate) mod client_capabilities;
pub mod methods;
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fmt;
use std::path::{Path, PathBuf};

use anyhow::Context;
Expand Down Expand Up @@ -62,3 +63,9 @@ impl Project for CairoProject {
}
}
}

impl fmt::Debug for CairoProject {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CairoProject").field("project_path", &self.project_path).finish()
}
}
7 changes: 7 additions & 0 deletions crates/cairo-lang-language-server/src/project/manager.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::cmp::Reverse;
use std::fmt;
use std::path::Path;

use cairo_lang_compiler::db::RootDatabase;
Expand Down Expand Up @@ -131,6 +132,12 @@ impl ProjectManager {
}
}

impl fmt::Debug for ProjectManager {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(&self.projects).finish()
}
}

fn is_cairo_file(path: &Path) -> bool {
path.extension().map_or(false, |ext| ext == CAIRO_FILE_EXTENSION)
}
Expand Down
3 changes: 2 additions & 1 deletion crates/cairo-lang-language-server/src/project/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fmt::Debug;
use std::path::Path;

use cairo_lang_compiler::db::RootDatabase;
Expand All @@ -14,7 +15,7 @@ mod unmanaged_core_crate;

// TODO(mkaput): Remove `Send` bound when migrating to new threading architecture.
/// A single Cairo project manager.
trait Project: Send {
trait Project: Debug + Send {
/// Gets a list of files that, when changed, should trigger a project reload.
///
/// This list may also include lockfiles.
Expand Down
8 changes: 8 additions & 0 deletions crates/cairo-lang-language-server/src/project/scarb/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fmt;
use std::path::{Path, PathBuf};

use anyhow::Context;
Expand Down Expand Up @@ -118,3 +119,10 @@ impl Project for ScarbWorkspace {
}
}
}

impl fmt::Debug for ScarbWorkspace {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO(mkaput): Implement this.
todo!();
}
}
1 change: 1 addition & 0 deletions crates/cairo-lang-language-server/tests/e2e/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod hover;
mod project;
mod semantic_tokens;
mod support;
mod workspace_configuration;
60 changes: 60 additions & 0 deletions crates/cairo-lang-language-server/tests/e2e/project.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use cairo_lang_language_server::lsp;
use indoc::indoc;

use crate::support::normalize::assert_normalized_eq;
use crate::support::sandbox;

#[test]
fn cairo_projects() {
let mut ls = sandbox! {
files {
"project1/cairo_project.toml" => indoc! {r#"
[crate_roots]
project1 = "src"
[config.global]
edition = "2023_11"
"#},
"project1/src/lib.cairo" => r#"fn main() {}"#,

"project2/cairo_project.toml" => indoc! {r#"
[crate_roots]
project2 = "src"
[config.global]
edition = "2023_11"
"#},
"project2/src/lib.cairo" => r#"fn main() {}"#,

"project2/subproject/cairo_project.toml" => indoc! {r#"
[crate_roots]
subproject = "src"
[config.global]
edition = "2023_11"
"#},
"project2/subproject/src/lib.cairo" => r#"fn main() {}"#,
}
};

ls.open_and_wait_for_diagnostics("project1/src/lib.cairo");
ls.open_and_wait_for_diagnostics("project2/src/lib.cairo");
ls.open_and_wait_for_diagnostics("project2/subproject/src/lib.cairo");

let projects = ls.send_request::<lsp::methods::test::DebugProjects>(());
assert_normalized_eq!(
&ls,
projects,
indoc! {r#"[
CairoProject {
project_path: "[ROOT]/project2/subproject/cairo_project.toml",
},
CairoProject {
project_path: "[ROOT]/project2/cairo_project.toml",
},
CairoProject {
project_path: "[ROOT]/project1/cairo_project.toml",
},
]"#}
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ impl Fixture {

/// Introspection methods.
impl Fixture {
pub fn root_path(&self) -> &Path {
self.t.path()
}

pub fn root_url(&self) -> Url {
Url::from_directory_path(self.t.path()).unwrap()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,9 @@ impl MockClient {
.collect()
}
}

impl AsRef<Fixture> for MockClient {
fn as_ref(&self) -> &Fixture {
&self.fixture
}
}
1 change: 1 addition & 0 deletions crates/cairo-lang-language-server/tests/e2e/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod cursor;
pub mod fixture;
pub mod jsonrpc;
mod mock_client;
pub mod normalize;
mod runtime;

pub use self::cursor::cursors;
Expand Down
45 changes: 45 additions & 0 deletions crates/cairo-lang-language-server/tests/e2e/support/normalize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::path::Path;

use crate::support::fixture::Fixture;

/// Like [`assert_eq`], but performs normalization of the left side.
macro_rules! assert_normalized_eq {
($fixture:expr, $actual:expr, $expected:expr $(,)?) => {
assert_eq!($crate::support::normalize::normalize($fixture, $actual), $expected,);
};
}

pub(crate) use assert_normalized_eq;

/// Performs various normalization steps of the input data, to remove any runtime-specific artifacts
/// and make comparisons in test assertions deterministic.
pub fn normalize(fixture: impl AsRef<Fixture>, data: impl ToString) -> String {
return inner(fixture.as_ref(), data.to_string());

fn inner(fixture: &Fixture, data: String) -> String {
normalize_well_known_paths(fixture, normalize_paths(data))
}
}

/// Replace all well-known paths/urls for a fixture with placeholders.
fn normalize_well_known_paths(fixture: &Fixture, data: String) -> String {
let mut data = data
.replace(&fixture.root_url().to_string(), "[ROOT_URL]")
.replace(&normalize_path(fixture.root_path()), "[ROOT]");

if let Ok(pwd) = std::env::current_dir() {
data = data.replace(&normalize_path(&pwd), "[PWD]");
}

data
}

/// Normalizes path separators.
fn normalize_paths(data: String) -> String {
data.replace('\\', "/")
}

/// Normalize a path to a consistent format.
fn normalize_path(path: &Path) -> String {
normalize_paths(path.to_string_lossy().to_string())
}

0 comments on commit 22e2c8a

Please sign in to comment.