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
|
self.wrap_map
|
||||||
.update(cx, |map, cx| map.set_wrap_width(width, cx))
|
.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 {
|
pub struct DisplayMapSnapshot {
|
||||||
|
@ -323,10 +328,139 @@ mod tests {
|
||||||
language::{Language, LanguageConfig},
|
language::{Language, LanguageConfig},
|
||||||
settings::Theme,
|
settings::Theme,
|
||||||
test::*,
|
test::*,
|
||||||
|
util::RandomCharIter,
|
||||||
};
|
};
|
||||||
use buffer::History;
|
use buffer::History;
|
||||||
use gpui::MutableAppContext;
|
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]
|
#[gpui::test]
|
||||||
async fn test_soft_wraps(mut cx: gpui::TestAppContext) {
|
async fn test_soft_wraps(mut cx: gpui::TestAppContext) {
|
||||||
|
|
|
@ -875,11 +875,10 @@ mod tests {
|
||||||
display_map::{fold_map::FoldMap, tab_map::TabMap},
|
display_map::{fold_map::FoldMap, tab_map::TabMap},
|
||||||
Buffer,
|
Buffer,
|
||||||
},
|
},
|
||||||
|
test::Observer,
|
||||||
util::RandomCharIter,
|
util::RandomCharIter,
|
||||||
};
|
};
|
||||||
use gpui::ModelHandle;
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use smol::channel;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
#[gpui::test(iterations = 100)]
|
#[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 crate::{fs::RealFs, language::LanguageRegistry, rpc, settings, time::ReplicaId, AppState};
|
||||||
use gpui::AppContext;
|
use gpui::{AppContext, Entity, ModelHandle};
|
||||||
|
use smol::channel;
|
||||||
use std::{
|
use std::{
|
||||||
|
marker::PhantomData,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
@ -155,3 +157,25 @@ pub fn build_app_state(cx: &AppContext) -> Arc<AppState> {
|
||||||
fs: Arc::new(RealFs),
|
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