Add unit test for diagnostic + path header ordering
This commit is contained in:
parent
6ad9ff10c1
commit
ed88fdcea2
3 changed files with 216 additions and 51 deletions
|
@ -160,11 +160,6 @@ impl ProjectDiagnosticsEditor {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn text(&self, cx: &AppContext) -> String {
|
|
||||||
self.editor.read(cx).text(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
|
fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
|
||||||
if let Some(existing) = workspace.item_of_type::<ProjectDiagnostics>(cx) {
|
if let Some(existing) = workspace.item_of_type::<ProjectDiagnostics>(cx) {
|
||||||
workspace.activate_item(&existing, cx);
|
workspace.activate_item(&existing, cx);
|
||||||
|
@ -611,7 +606,7 @@ fn path_header_renderer(buffer: ModelHandle<Buffer>, build_settings: BuildSettin
|
||||||
.with_style(style.header)
|
.with_style(style.header)
|
||||||
.with_padding_left(cx.line_number_x)
|
.with_padding_left(cx.line_number_x)
|
||||||
.expanded()
|
.expanded()
|
||||||
.boxed()
|
.named("path header block")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,7 +628,7 @@ fn diagnostic_header_renderer(
|
||||||
.with_style(diagnostic_style.header)
|
.with_style(diagnostic_style.header)
|
||||||
.with_padding_left(cx.line_number_x)
|
.with_padding_left(cx.line_number_x)
|
||||||
.expanded()
|
.expanded()
|
||||||
.boxed()
|
.named("diagnostic header")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,7 +639,7 @@ fn context_header_renderer(build_settings: BuildSettings) -> RenderBlock {
|
||||||
Label::new("…".to_string(), text_style)
|
Label::new("…".to_string(), text_style)
|
||||||
.contained()
|
.contained()
|
||||||
.with_padding_left(cx.line_number_x)
|
.with_padding_left(cx.line_number_x)
|
||||||
.boxed()
|
.named("collapsed context")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,11 +664,10 @@ fn compare_diagnostics<L: language::ToOffset, R: language::ToOffset>(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use client::{http::ServerResponse, test::FakeHttpClient, Client, UserStore};
|
use editor::{display_map::BlockContext, DisplayPoint, EditorSnapshot};
|
||||||
use editor::DisplayPoint;
|
|
||||||
use gpui::TestAppContext;
|
use gpui::TestAppContext;
|
||||||
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, LanguageRegistry, PointUtf16};
|
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16};
|
||||||
use project::{worktree, FakeFs};
|
use project::worktree;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use unindent::Unindent as _;
|
use unindent::Unindent as _;
|
||||||
|
@ -681,29 +675,21 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_diagnostics(mut cx: TestAppContext) {
|
async fn test_diagnostics(mut cx: TestAppContext) {
|
||||||
let workspace_params = cx.update(WorkspaceParams::test);
|
let params = cx.update(WorkspaceParams::test);
|
||||||
let settings = workspace_params.settings.clone();
|
let project = params.project.clone();
|
||||||
let http_client = FakeHttpClient::new(|_| async move { Ok(ServerResponse::new(404)) });
|
let workspace = cx.add_view(0, |cx| Workspace::new(¶ms, cx));
|
||||||
let client = Client::new(http_client.clone());
|
|
||||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
|
||||||
let fs = Arc::new(FakeFs::new());
|
|
||||||
|
|
||||||
let project = cx.update(|cx| {
|
params
|
||||||
Project::local(
|
.fs
|
||||||
client.clone(),
|
.as_fake()
|
||||||
user_store,
|
.insert_tree(
|
||||||
Arc::new(LanguageRegistry::new()),
|
|
||||||
fs.clone(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.insert_tree(
|
|
||||||
"/test",
|
"/test",
|
||||||
json!({
|
json!({
|
||||||
"a.rs": "
|
"consts.rs": "
|
||||||
const a: i32 = 'a';
|
const a: i32 = 'a';
|
||||||
".unindent(),
|
const b: i32 = c;
|
||||||
|
"
|
||||||
|
.unindent(),
|
||||||
|
|
||||||
"main.rs": "
|
"main.rs": "
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -729,6 +715,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// Create some diagnostics
|
||||||
worktree.update(&mut cx, |worktree, cx| {
|
worktree.update(&mut cx, |worktree, cx| {
|
||||||
worktree
|
worktree
|
||||||
.update_diagnostic_entries(
|
.update_diagnostic_entries(
|
||||||
|
@ -811,19 +798,25 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Open the project diagnostics view while there are already diagnostics.
|
||||||
let model = cx.add_model(|_| ProjectDiagnostics::new(project.clone()));
|
let model = cx.add_model(|_| ProjectDiagnostics::new(project.clone()));
|
||||||
let workspace = cx.add_view(0, |cx| Workspace::new(&workspace_params, cx));
|
|
||||||
|
|
||||||
let view = cx.add_view(0, |cx| {
|
let view = cx.add_view(0, |cx| {
|
||||||
ProjectDiagnosticsEditor::new(model, workspace.downgrade(), settings, cx)
|
ProjectDiagnosticsEditor::new(model, workspace.downgrade(), params.settings, cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
view.condition(&mut cx, |view, cx| view.text(cx).contains("fn main()"))
|
view.next_notification(&cx).await;
|
||||||
.await;
|
|
||||||
|
|
||||||
view.update(&mut cx, |view, cx| {
|
view.update(&mut cx, |view, cx| {
|
||||||
let editor = view.editor.update(cx, |editor, cx| editor.snapshot(cx));
|
let editor = view.editor.update(cx, |editor, cx| editor.snapshot(cx));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
editor_blocks(&editor, cx),
|
||||||
|
[
|
||||||
|
(0, "path header block".into()),
|
||||||
|
(2, "diagnostic header".into()),
|
||||||
|
(15, "diagnostic header".into()),
|
||||||
|
(24, "collapsed context".into()),
|
||||||
|
]
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor.text(),
|
editor.text(),
|
||||||
concat!(
|
concat!(
|
||||||
|
@ -864,6 +857,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Cursor is at the first diagnostic
|
||||||
view.editor.update(cx, |editor, cx| {
|
view.editor.update(cx, |editor, cx| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor.selected_display_ranges(cx),
|
editor.selected_display_ranges(cx),
|
||||||
|
@ -872,10 +866,11 @@ mod tests {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Diagnostics are added for another earlier path.
|
||||||
worktree.update(&mut cx, |worktree, cx| {
|
worktree.update(&mut cx, |worktree, cx| {
|
||||||
worktree
|
worktree
|
||||||
.update_diagnostic_entries(
|
.update_diagnostic_entries(
|
||||||
Arc::from("/test/a.rs".as_ref()),
|
Arc::from("/test/consts.rs".as_ref()),
|
||||||
None,
|
None,
|
||||||
vec![DiagnosticEntry {
|
vec![DiagnosticEntry {
|
||||||
range: PointUtf16::new(0, 15)..PointUtf16::new(0, 15),
|
range: PointUtf16::new(0, 15)..PointUtf16::new(0, 15),
|
||||||
|
@ -894,17 +889,26 @@ mod tests {
|
||||||
cx.emit(worktree::Event::DiskBasedDiagnosticsUpdated);
|
cx.emit(worktree::Event::DiskBasedDiagnosticsUpdated);
|
||||||
});
|
});
|
||||||
|
|
||||||
view.condition(&mut cx, |view, cx| view.text(cx).contains("const a"))
|
view.next_notification(&cx).await;
|
||||||
.await;
|
|
||||||
|
|
||||||
view.update(&mut cx, |view, cx| {
|
view.update(&mut cx, |view, cx| {
|
||||||
let editor = view.editor.update(cx, |editor, cx| editor.snapshot(cx));
|
let editor = view.editor.update(cx, |editor, cx| editor.snapshot(cx));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
editor_blocks(&editor, cx),
|
||||||
|
[
|
||||||
|
(0, "path header block".into()),
|
||||||
|
(2, "diagnostic header".into()),
|
||||||
|
(7, "path header block".into()),
|
||||||
|
(9, "diagnostic header".into()),
|
||||||
|
(22, "diagnostic header".into()),
|
||||||
|
(31, "collapsed context".into()),
|
||||||
|
]
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
editor.text(),
|
editor.text(),
|
||||||
concat!(
|
concat!(
|
||||||
//
|
//
|
||||||
// a.rs
|
// consts.rs
|
||||||
//
|
//
|
||||||
"\n", // filename
|
"\n", // filename
|
||||||
"\n", // padding
|
"\n", // padding
|
||||||
|
@ -913,7 +917,126 @@ mod tests {
|
||||||
"\n", // padding
|
"\n", // padding
|
||||||
"const a: i32 = 'a';\n",
|
"const a: i32 = 'a';\n",
|
||||||
"\n", // supporting diagnostic
|
"\n", // supporting diagnostic
|
||||||
"\n", // context line
|
"const b: i32 = c;\n",
|
||||||
|
//
|
||||||
|
// main.rs
|
||||||
|
//
|
||||||
|
"\n", // filename
|
||||||
|
"\n", // padding
|
||||||
|
// diagnostic group 1
|
||||||
|
"\n", // primary message
|
||||||
|
"\n", // padding
|
||||||
|
" let x = vec![];\n",
|
||||||
|
" let y = vec![];\n",
|
||||||
|
"\n", // supporting diagnostic
|
||||||
|
" a(x);\n",
|
||||||
|
" b(y);\n",
|
||||||
|
"\n", // supporting diagnostic
|
||||||
|
" // comment 1\n",
|
||||||
|
" // comment 2\n",
|
||||||
|
" c(y);\n",
|
||||||
|
"\n", // supporting diagnostic
|
||||||
|
" d(x);\n",
|
||||||
|
// diagnostic group 2
|
||||||
|
"\n", // primary message
|
||||||
|
"\n", // filename
|
||||||
|
"fn main() {\n",
|
||||||
|
" let x = vec![];\n",
|
||||||
|
"\n", // supporting diagnostic
|
||||||
|
" let y = vec![];\n",
|
||||||
|
" a(x);\n",
|
||||||
|
"\n", // supporting diagnostic
|
||||||
|
" b(y);\n",
|
||||||
|
"\n", // context ellipsis
|
||||||
|
" c(y);\n",
|
||||||
|
" d(x);\n",
|
||||||
|
"\n", // supporting diagnostic
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cursor keeps its position.
|
||||||
|
view.editor.update(cx, |editor, cx| {
|
||||||
|
assert_eq!(
|
||||||
|
editor.selected_display_ranges(cx),
|
||||||
|
[DisplayPoint::new(19, 6)..DisplayPoint::new(19, 6)]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Diagnostics are added to the first path
|
||||||
|
worktree.update(&mut cx, |worktree, cx| {
|
||||||
|
worktree
|
||||||
|
.update_diagnostic_entries(
|
||||||
|
Arc::from("/test/consts.rs".as_ref()),
|
||||||
|
None,
|
||||||
|
vec![
|
||||||
|
DiagnosticEntry {
|
||||||
|
range: PointUtf16::new(0, 15)..PointUtf16::new(0, 15),
|
||||||
|
diagnostic: Diagnostic {
|
||||||
|
message: "mismatched types\nexpected `usize`, found `char`"
|
||||||
|
.to_string(),
|
||||||
|
severity: DiagnosticSeverity::ERROR,
|
||||||
|
is_primary: true,
|
||||||
|
is_disk_based: true,
|
||||||
|
group_id: 0,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DiagnosticEntry {
|
||||||
|
range: PointUtf16::new(1, 15)..PointUtf16::new(1, 15),
|
||||||
|
diagnostic: Diagnostic {
|
||||||
|
message: "unresolved name `c`".to_string(),
|
||||||
|
severity: DiagnosticSeverity::ERROR,
|
||||||
|
is_primary: true,
|
||||||
|
is_disk_based: true,
|
||||||
|
group_id: 1,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
cx.emit(worktree::Event::DiskBasedDiagnosticsUpdated);
|
||||||
|
});
|
||||||
|
|
||||||
|
view.next_notification(&cx).await;
|
||||||
|
view.update(&mut cx, |view, cx| {
|
||||||
|
let editor = view.editor.update(cx, |editor, cx| editor.snapshot(cx));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
editor_blocks(&editor, cx),
|
||||||
|
[
|
||||||
|
(0, "path header block".into()),
|
||||||
|
(2, "diagnostic header".into()),
|
||||||
|
(7, "diagnostic header".into()),
|
||||||
|
(12, "path header block".into()),
|
||||||
|
(14, "diagnostic header".into()),
|
||||||
|
(27, "diagnostic header".into()),
|
||||||
|
(36, "collapsed context".into()),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
editor.text(),
|
||||||
|
concat!(
|
||||||
|
//
|
||||||
|
// consts.rs
|
||||||
|
//
|
||||||
|
"\n", // filename
|
||||||
|
"\n", // padding
|
||||||
|
// diagnostic group 1
|
||||||
|
"\n", // primary message
|
||||||
|
"\n", // padding
|
||||||
|
"const a: i32 = 'a';\n",
|
||||||
|
"\n", // supporting diagnostic
|
||||||
|
"const b: i32 = c;\n",
|
||||||
|
// diagnostic group 2
|
||||||
|
"\n", // primary message
|
||||||
|
"\n", // padding
|
||||||
|
"const a: i32 = 'a';\n",
|
||||||
|
"const b: i32 = c;\n",
|
||||||
|
"\n", // supporting diagnostic
|
||||||
//
|
//
|
||||||
// main.rs
|
// main.rs
|
||||||
//
|
//
|
||||||
|
@ -952,4 +1075,20 @@ mod tests {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn editor_blocks(editor: &EditorSnapshot, cx: &AppContext) -> Vec<(u32, String)> {
|
||||||
|
editor
|
||||||
|
.blocks_in_range(0..editor.max_point().row())
|
||||||
|
.filter_map(|(row, block)| {
|
||||||
|
block
|
||||||
|
.render(&BlockContext {
|
||||||
|
cx,
|
||||||
|
anchor_x: 0.,
|
||||||
|
line_number_x: 0.,
|
||||||
|
})
|
||||||
|
.name()
|
||||||
|
.map(|s| (row, s.to_string()))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2870,6 +2870,28 @@ impl<T: View> ViewHandle<T> {
|
||||||
.map_or(false, |focused_id| focused_id == self.view_id)
|
.map_or(false, |focused_id| focused_id == self.view_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn next_notification(&self, cx: &TestAppContext) -> impl Future<Output = ()> {
|
||||||
|
let (mut tx, mut rx) = mpsc::channel(1);
|
||||||
|
let mut cx = cx.cx.borrow_mut();
|
||||||
|
let subscription = cx.observe(self, move |_, _| {
|
||||||
|
tx.blocking_send(()).ok();
|
||||||
|
});
|
||||||
|
|
||||||
|
let duration = if std::env::var("CI").is_ok() {
|
||||||
|
Duration::from_secs(5)
|
||||||
|
} else {
|
||||||
|
Duration::from_secs(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
async move {
|
||||||
|
let notification = timeout(duration, rx.recv())
|
||||||
|
.await
|
||||||
|
.expect("next notification timed out");
|
||||||
|
drop(subscription);
|
||||||
|
notification.expect("model dropped while test was waiting for its next notification")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn condition(
|
pub fn condition(
|
||||||
&self,
|
&self,
|
||||||
cx: &TestAppContext,
|
cx: &TestAppContext,
|
||||||
|
|
|
@ -980,7 +980,11 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_next_pane(&mut self, cx: &mut ViewContext<Self>) {
|
pub fn activate_next_pane(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
let ix = self.panes.iter().position(|pane| pane == &self.active_pane).unwrap();
|
let ix = self
|
||||||
|
.panes
|
||||||
|
.iter()
|
||||||
|
.position(|pane| pane == &self.active_pane)
|
||||||
|
.unwrap();
|
||||||
let next_ix = (ix + 1) % self.panes.len();
|
let next_ix = (ix + 1) % self.panes.len();
|
||||||
self.activate_pane(self.panes[next_ix].clone(), cx);
|
self.activate_pane(self.panes[next_ix].clone(), cx);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue