Add randomized test for DisplayMap
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
576cabea93
commit
c1808d09ef
3 changed files with 161 additions and 26 deletions
|
@ -89,6 +89,11 @@ impl DisplayMap {
|
|||
self.wrap_map
|
||||
.update(cx, |map, cx| map.set_wrap_width(width, cx))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn is_rewrapping(&self, cx: &gpui::AppContext) -> bool {
|
||||
self.wrap_map.read(cx).is_rewrapping()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DisplayMapSnapshot {
|
||||
|
@ -323,10 +328,139 @@ mod tests {
|
|||
language::{Language, LanguageConfig},
|
||||
settings::Theme,
|
||||
test::*,
|
||||
util::RandomCharIter,
|
||||
};
|
||||
use buffer::History;
|
||||
use gpui::MutableAppContext;
|
||||
use std::sync::Arc;
|
||||
use rand::{prelude::StdRng, Rng};
|
||||
use std::{env, sync::Arc};
|
||||
use Bias::*;
|
||||
|
||||
#[gpui::test(iterations = 100, seed = 495)]
|
||||
async fn test_random(mut cx: gpui::TestAppContext, mut rng: StdRng) {
|
||||
cx.foreground().set_block_on_ticks(0..=50);
|
||||
cx.foreground().forbid_parking();
|
||||
let operations = env::var("OPERATIONS")
|
||||
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
|
||||
.unwrap_or(10);
|
||||
|
||||
let font_cache = cx.font_cache().clone();
|
||||
let settings = Settings {
|
||||
tab_size: rng.gen_range(1..=4),
|
||||
buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
|
||||
buffer_font_size: 14.0,
|
||||
..Settings::new(&font_cache).unwrap()
|
||||
};
|
||||
let max_wrap_width = 300.0;
|
||||
let mut wrap_width = if rng.gen_bool(0.1) {
|
||||
None
|
||||
} else {
|
||||
Some(rng.gen_range(0.0..=max_wrap_width))
|
||||
};
|
||||
|
||||
log::info!("tab size: {}", settings.tab_size);
|
||||
log::info!("wrap width: {:?}", wrap_width);
|
||||
|
||||
let buffer = cx.add_model(|cx| {
|
||||
let len = rng.gen_range(0..10);
|
||||
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
|
||||
Buffer::new(0, text, cx)
|
||||
});
|
||||
|
||||
let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx));
|
||||
let (_observer, notifications) = Observer::new(&map, &mut cx);
|
||||
|
||||
for _i in 0..operations {
|
||||
match rng.gen_range(0..=100) {
|
||||
0..=19 => {
|
||||
wrap_width = if rng.gen_bool(0.2) {
|
||||
None
|
||||
} else {
|
||||
Some(rng.gen_range(0.0..=max_wrap_width))
|
||||
};
|
||||
log::info!("setting wrap width to {:?}", wrap_width);
|
||||
map.update(&mut cx, |map, cx| map.set_wrap_width(wrap_width, cx));
|
||||
}
|
||||
20..=39 => {
|
||||
let mut ranges = Vec::new();
|
||||
for _ in 0..rng.gen_range(1..=3) {
|
||||
buffer.read_with(&cx, |buffer, _| {
|
||||
let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
|
||||
let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
|
||||
ranges.push(start..end);
|
||||
});
|
||||
}
|
||||
|
||||
if rng.gen() {
|
||||
log::info!("unfolding ranges: {:?}", ranges);
|
||||
map.update(&mut cx, |map, cx| {
|
||||
map.unfold(ranges, cx);
|
||||
});
|
||||
} else {
|
||||
log::info!("folding ranges: {:?}", ranges);
|
||||
map.update(&mut cx, |map, cx| {
|
||||
map.fold(ranges, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
buffer.update(&mut cx, |buffer, cx| buffer.randomly_mutate(&mut rng, cx));
|
||||
}
|
||||
}
|
||||
|
||||
if map.read_with(&cx, |map, cx| map.is_rewrapping(cx)) {
|
||||
notifications.recv().await.unwrap();
|
||||
}
|
||||
|
||||
let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx));
|
||||
log::info!("buffer text: {:?}", buffer.read_with(&cx, |b, _| b.text()));
|
||||
log::info!("display text: {:?}", snapshot.text());
|
||||
|
||||
// line boundaries
|
||||
for _ in 0..5 {
|
||||
let row = rng.gen_range(0..=snapshot.max_point().row());
|
||||
let column = rng.gen_range(0..=snapshot.line_len(row));
|
||||
let point = snapshot.clip_point(DisplayPoint::new(row, column), Left);
|
||||
|
||||
let (prev_display_bound, prev_buffer_bound) = snapshot.prev_row_boundary(point);
|
||||
let (next_display_bound, next_buffer_bound) = snapshot.next_row_boundary(point);
|
||||
|
||||
assert!(prev_display_bound <= point);
|
||||
assert!(next_display_bound >= point);
|
||||
assert_eq!(prev_buffer_bound.column, 0);
|
||||
assert_eq!(prev_display_bound.column(), 0);
|
||||
if next_display_bound < snapshot.max_point() {
|
||||
assert_eq!(next_buffer_bound.column, 0);
|
||||
assert_eq!(next_display_bound.column(), 0);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
prev_buffer_bound.to_display_point(&snapshot),
|
||||
prev_display_bound,
|
||||
"{:?} to display point",
|
||||
prev_buffer_bound
|
||||
);
|
||||
assert_eq!(
|
||||
next_buffer_bound.to_display_point(&snapshot),
|
||||
next_display_bound,
|
||||
"{:?} to display point",
|
||||
next_buffer_bound
|
||||
);
|
||||
assert_eq!(
|
||||
prev_display_bound.to_buffer_point(&snapshot, Left),
|
||||
prev_buffer_bound,
|
||||
"{:?} to buffer point",
|
||||
prev_display_bound
|
||||
);
|
||||
assert_eq!(
|
||||
next_display_bound.to_buffer_point(&snapshot, Left),
|
||||
next_buffer_bound,
|
||||
"{:?} to buffer point",
|
||||
next_display_bound
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_soft_wraps(mut cx: gpui::TestAppContext) {
|
||||
|
|
|
@ -875,11 +875,10 @@ mod tests {
|
|||
display_map::{fold_map::FoldMap, tab_map::TabMap},
|
||||
Buffer,
|
||||
},
|
||||
test::Observer,
|
||||
util::RandomCharIter,
|
||||
};
|
||||
use gpui::ModelHandle;
|
||||
use rand::prelude::*;
|
||||
use smol::channel;
|
||||
use std::env;
|
||||
|
||||
#[gpui::test(iterations = 100)]
|
||||
|
@ -1077,26 +1076,4 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Observer;
|
||||
|
||||
impl Entity for Observer {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl Observer {
|
||||
fn new(
|
||||
handle: &ModelHandle<WrapMap>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) -> (ModelHandle<Self>, channel::Receiver<()>) {
|
||||
let (notify_tx, notify_rx) = channel::unbounded();
|
||||
let observer = cx.add_model(|cx| {
|
||||
cx.observe(handle, move |_, _, _| {
|
||||
let _ = notify_tx.try_send(());
|
||||
});
|
||||
Observer
|
||||
});
|
||||
(observer, notify_rx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::{fs::RealFs, language::LanguageRegistry, rpc, settings, time::ReplicaId, AppState};
|
||||
use gpui::AppContext;
|
||||
use gpui::{AppContext, Entity, ModelHandle};
|
||||
use smol::channel;
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
@ -155,3 +157,25 @@ pub fn build_app_state(cx: &AppContext) -> Arc<AppState> {
|
|||
fs: Arc::new(RealFs),
|
||||
})
|
||||
}
|
||||
|
||||
pub struct Observer<T>(PhantomData<T>);
|
||||
|
||||
impl<T: 'static + Send + Sync> Entity for Observer<T> {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl<T: Entity> Observer<T> {
|
||||
pub fn new(
|
||||
handle: &ModelHandle<T>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) -> (ModelHandle<Self>, channel::Receiver<()>) {
|
||||
let (notify_tx, notify_rx) = channel::unbounded();
|
||||
let observer = cx.add_model(|cx| {
|
||||
cx.observe(handle, move |_, _, _| {
|
||||
let _ = notify_tx.try_send(());
|
||||
});
|
||||
Observer(PhantomData)
|
||||
});
|
||||
(observer, notify_rx)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue