From 62ad97a728ac14eb82249b388d2436a43712efcf Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 21 Jul 2021 14:41:04 +0200 Subject: [PATCH] Add randomized test for `DisplayMap::buffer_rows` and fix logic errors --- zed/src/editor/display_map.rs | 66 +++++++++++++++++++++++++- zed/src/editor/display_map/wrap_map.rs | 31 ++++++------ zed/src/util.rs | 6 ++- 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/zed/src/editor/display_map.rs b/zed/src/editor/display_map.rs index 4437407d9f353d4f89af98e1505da7c49d419978..77b4935127016422f0ed3adbec6454fba3e0df10 100644 --- a/zed/src/editor/display_map.rs +++ b/zed/src/editor/display_map.rs @@ -272,9 +272,73 @@ mod tests { language::{Language, LanguageConfig}, settings::Theme, test::*, + util::RandomCharIter, }; use buffer::History; - use std::sync::Arc; + use rand::prelude::*; + use std::{env, sync::Arc}; + + #[gpui::test] + async fn test_random(mut cx: gpui::TestAppContext) { + cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); + let iterations = env::var("ITERATIONS") + .map(|i| i.parse().expect("invalid `ITERATIONS` variable")) + .unwrap_or(100); + let operations = env::var("OPERATIONS") + .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) + .unwrap_or(10); + let seed_range = if let Ok(seed) = env::var("SEED") { + let seed = seed.parse().expect("invalid `SEED` variable"); + seed..seed + 1 + } else { + 0..iterations + }; + let font_cache = cx.font_cache(); + + for seed in seed_range { + dbg!(seed); + let mut rng = StdRng::seed_from_u64(seed); + let settings = Settings { + buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(), + ui_font_family: font_cache.load_family(&["Helvetica"]).unwrap(), + buffer_font_size: 12.0, + ui_font_size: 12.0, + tab_size: rng.gen_range(1..=4), + theme: Arc::new(Theme::default()), + }; + + let buffer = cx.add_model(|cx| { + let len = rng.gen_range(0..10); + let text = RandomCharIter::new(&mut rng).take(len).collect::(); + log::info!("Initial buffer text: {:?}", text); + Buffer::new(0, text, cx) + }); + let wrap_width = Some(rng.gen_range(20.0..=100.0)); + let map = cx.read(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx)); + + for _op_ix in 0..operations { + buffer.update(&mut cx, |buffer, cx| buffer.randomly_mutate(&mut rng, cx)); + let snapshot = cx.read(|cx| map.snapshot(cx)); + let expected_buffer_rows = (0..=snapshot.max_point().row()) + .map(|display_row| { + DisplayPoint::new(display_row, 0) + .to_buffer_point(&snapshot, Bias::Left) + .row + }) + .collect::>(); + for start_display_row in 0..expected_buffer_rows.len() { + assert_eq!( + snapshot + .buffer_rows(start_display_row as u32) + .collect::>(), + &expected_buffer_rows[start_display_row..], + "invalid buffer_rows({}..)", + start_display_row + ); + } + } + } + } #[gpui::test] async fn test_soft_wraps(mut cx: gpui::TestAppContext) { diff --git a/zed/src/editor/display_map/wrap_map.rs b/zed/src/editor/display_map/wrap_map.rs index 4385f7a815599e56dd4cc0c1af6f8466fa2dc55e..ba0fbc75af73e8052ec6ba5ada215dc83bfc12e6 100644 --- a/zed/src/editor/display_map/wrap_map.rs +++ b/zed/src/editor/display_map/wrap_map.rs @@ -167,14 +167,15 @@ impl Snapshot { pub fn buffer_rows(&self, start_row: u32) -> BufferRows { let mut transforms = self.transforms.cursor::(); transforms.seek(&OutputPoint::new(start_row, 0), Bias::Right, &()); - let input_row = transforms.sum_start().row(); - let mut input_buffer_rows = self.input.buffer_rows(start_row); + let input_row = transforms.sum_start().row() + (start_row - transforms.seek_start().row()); + let mut input_buffer_rows = self.input.buffer_rows(input_row); let input_buffer_row = input_buffer_rows.next().unwrap(); BufferRows { transforms, - input_row, input_buffer_row, input_buffer_rows, + output_row: start_row, + max_output_row: self.max_point().row(), } } @@ -212,8 +213,9 @@ pub struct HighlightedChunks<'a> { pub struct BufferRows<'a> { input_buffer_rows: fold_map::BufferRows<'a>, - input_row: u32, input_buffer_row: u32, + output_row: u32, + max_output_row: u32, transforms: Cursor<'a, Transform, OutputPoint, InputPoint>, } @@ -299,18 +301,19 @@ impl<'a> Iterator for BufferRows<'a> { type Item = u32; fn next(&mut self) -> Option { - let result = self.input_buffer_row; - if self.input_row + 1 < self.transforms.sum_end(&()).row() { - self.input_row += 1; + if self.output_row > self.max_output_row { + return None; + } + + let buffer_row = self.input_buffer_row; + self.output_row += 1; + self.transforms + .seek_forward(&OutputPoint::new(self.output_row, 0), Bias::Left, &()); + if self.transforms.item().map_or(false, |t| t.is_isomorphic()) { self.input_buffer_row = self.input_buffer_rows.next().unwrap(); - } else { - self.transforms.seek_forward( - &OutputPoint::new(self.transforms.seek_start().row() + 1, 0), - Bias::Right, - &(), - ); } - Some(result) + + Some(buffer_row) } } diff --git a/zed/src/util.rs b/zed/src/util.rs index 0fa1081d66704270fd00207b1ad813d0ed99e405..d150e4a77d928340cfb1262e713022990a505c02 100644 --- a/zed/src/util.rs +++ b/zed/src/util.rs @@ -67,7 +67,11 @@ impl Iterator for RandomCharIter { fn next(&mut self) -> Option { if self.0.gen_bool(1.0 / 5.0) { - Some('\n') + match self.0.gen_range(0..=2) { + 0 => Some('\t'), + 1 => Some(' '), + _ => Some('\n'), + } } // two-byte greek letters else if self.0.gen_bool(1.0 / 8.0) {