Use NoopTextSystem during tests (#28607)

Conrad Irwin created

This should allow tests to be more similar across platforms.

Release Notes:

- N/A

Change summary

crates/editor/src/display_map.rs            |   5 
crates/editor/src/display_map/block_map.rs  |   3 
crates/editor/src/editor_tests.rs           |   2 
crates/editor/src/element.rs                |   2 
crates/gpui/src/platform.rs                 | 104 ++++++++++++++++++----
crates/gpui/src/platform/test/platform.rs   |  18 ---
crates/gpui/src/text_system/line_wrapper.rs |  19 +--
7 files changed, 98 insertions(+), 55 deletions(-)

Detailed changes

crates/editor/src/display_map.rs 🔗

@@ -1742,7 +1742,6 @@ pub mod tests {
         }
     }
 
-    #[cfg(target_os = "macos")]
     #[gpui::test(retries = 5)]
     async fn test_soft_wraps(cx: &mut gpui::TestAppContext) {
         cx.background_executor
@@ -1760,7 +1759,7 @@ pub mod tests {
                 editor.update(cx, |editor, _cx| editor.text_layout_details(window));
 
             let font_size = px(12.0);
-            let wrap_width = Some(px(64.));
+            let wrap_width = Some(px(96.));
 
             let text = "one two three four five\nsix seven eight";
             let buffer = MultiBuffer::build_simple(text, cx);
@@ -2411,8 +2410,6 @@ pub mod tests {
         }
     }
 
-    // todo(linux) fails due to pixel differences in text rendering
-    #[cfg(target_os = "macos")]
     #[gpui::test]
     async fn test_chunks_with_soft_wrapping(cx: &mut gpui::TestAppContext) {
         cx.background_executor

crates/editor/src/display_map/block_map.rs 🔗

@@ -2277,7 +2277,6 @@ mod tests {
         }
     }
 
-    #[cfg(target_os = "macos")]
     #[gpui::test]
     fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
         cx.update(init_test);
@@ -2292,7 +2291,7 @@ mod tests {
         let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
         let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
         let (_, wraps_snapshot) = cx.update(|cx| {
-            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
+            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(90.)), cx)
         });
         let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
 

crates/editor/src/editor_tests.rs 🔗

@@ -1316,8 +1316,6 @@ fn test_move_cursor(cx: &mut TestAppContext) {
     });
 }
 
-// TODO: Re-enable this test
-#[cfg(target_os = "macos")]
 #[gpui::test]
 fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
     init_test(cx, |_| {});

crates/editor/src/element.rs 🔗

@@ -6638,7 +6638,7 @@ impl Element for EditorElement {
                                 }
                             };
 
-                            if editor.set_wrap_width(wrap_width, cx) {
+                            if editor.set_wrap_width(wrap_width.map(|w| w.ceil()), cx) {
                                 editor.snapshot(window, cx)
                             } else {
                                 snapshot

crates/gpui/src/platform.rs 🔗

@@ -37,9 +37,10 @@ use crate::{
     DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
     ForegroundExecutor, GlyphId, GpuSpecs, ImageSource, Keymap, LineLayout, Pixels, PlatformInput,
     Point, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
-    SharedString, Size, SvgRenderer, SvgSize, Task, TaskLabel, Window, point,
+    ShapedGlyph, ShapedRun, SharedString, Size, SvgRenderer, SvgSize, Task, TaskLabel, Window,
+    point, px, size,
 };
-use anyhow::{Result, anyhow};
+use anyhow::Result;
 use async_task::Runnable;
 use futures::channel::oneshot;
 use image::codecs::gif::GifDecoder;
@@ -532,40 +533,105 @@ impl PlatformTextSystem for NoopTextSystem {
         Vec::new()
     }
 
-    fn font_id(&self, descriptor: &Font) -> Result<FontId> {
-        Err(anyhow!("No font found for {:?}", descriptor))
+    fn font_id(&self, _descriptor: &Font) -> Result<FontId> {
+        return Ok(FontId(1));
     }
 
     fn font_metrics(&self, _font_id: FontId) -> FontMetrics {
-        unimplemented!()
+        FontMetrics {
+            units_per_em: 1000,
+            ascent: 1025.0,
+            descent: -275.0,
+            line_gap: 0.0,
+            underline_position: -95.0,
+            underline_thickness: 60.0,
+            cap_height: 698.0,
+            x_height: 516.0,
+            bounding_box: Bounds {
+                origin: Point {
+                    x: -260.0,
+                    y: -245.0,
+                },
+                size: Size {
+                    width: 1501.0,
+                    height: 1364.0,
+                },
+            },
+        }
     }
 
-    fn typographic_bounds(&self, font_id: FontId, _glyph_id: GlyphId) -> Result<Bounds<f32>> {
-        Err(anyhow!("No font found for {:?}", font_id))
+    fn typographic_bounds(&self, _font_id: FontId, _glyph_id: GlyphId) -> Result<Bounds<f32>> {
+        Ok(Bounds {
+            origin: Point { x: 54.0, y: 0.0 },
+            size: size(392.0, 528.0),
+        })
     }
 
-    fn advance(&self, font_id: FontId, _glyph_id: GlyphId) -> Result<Size<f32>> {
-        Err(anyhow!("No font found for {:?}", font_id))
+    fn advance(&self, _font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
+        Ok(size(600.0 * glyph_id.0 as f32, 0.0))
     }
 
-    fn glyph_for_char(&self, _font_id: FontId, _ch: char) -> Option<GlyphId> {
-        None
+    fn glyph_for_char(&self, _font_id: FontId, ch: char) -> Option<GlyphId> {
+        Some(GlyphId(ch.len_utf16() as u32))
     }
 
-    fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
-        Err(anyhow!("No font found for {:?}", params))
+    fn glyph_raster_bounds(&self, _params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
+        Ok(Default::default())
     }
 
     fn rasterize_glyph(
         &self,
-        params: &RenderGlyphParams,
-        _raster_bounds: Bounds<DevicePixels>,
+        _params: &RenderGlyphParams,
+        raster_bounds: Bounds<DevicePixels>,
     ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
-        Err(anyhow!("No font found for {:?}", params))
-    }
+        Ok((raster_bounds.size, Vec::new()))
+    }
+
+    fn layout_line(&self, text: &str, font_size: Pixels, _runs: &[FontRun]) -> LineLayout {
+        let mut position = px(0.);
+        let metrics = self.font_metrics(FontId(0));
+        let em_width = font_size
+            * self
+                .advance(FontId(0), self.glyph_for_char(FontId(0), 'm').unwrap())
+                .unwrap()
+                .width
+            / metrics.units_per_em as f32;
+        let mut glyphs = SmallVec::default();
+        for (ix, c) in text.char_indices() {
+            if let Some(glyph) = self.glyph_for_char(FontId(0), c) {
+                glyphs.push(ShapedGlyph {
+                    id: glyph,
+                    position: point(position, px(0.)),
+                    index: ix,
+                    is_emoji: glyph.0 == 2,
+                });
+                if glyph.0 == 2 {
+                    position += em_width * 2.0;
+                } else {
+                    position += em_width;
+                }
+            } else {
+                position += em_width
+            }
+        }
+        let mut runs = Vec::default();
+        if glyphs.len() > 0 {
+            runs.push(ShapedRun {
+                font_id: FontId(0),
+                glyphs,
+            });
+        } else {
+            position = px(0.);
+        }
 
-    fn layout_line(&self, _text: &str, _font_size: Pixels, _runs: &[FontRun]) -> LineLayout {
-        unimplemented!()
+        LineLayout {
+            font_size,
+            width: position,
+            ascent: font_size * (metrics.ascent / metrics.units_per_em as f32),
+            descent: font_size * (metrics.descent / metrics.units_per_em as f32),
+            runs,
+            len: text.len(),
+        }
     }
 }
 

crates/gpui/src/platform/test/platform.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
     AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels,
-    ForegroundExecutor, Keymap, Platform, PlatformDisplay, PlatformTextSystem, ScreenCaptureFrame,
-    ScreenCaptureSource, ScreenCaptureStream, Size, Task, TestDisplay, TestWindow,
-    WindowAppearance, WindowParams, size,
+    ForegroundExecutor, Keymap, NoopTextSystem, Platform, PlatformDisplay, PlatformTextSystem,
+    ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, Size, Task, TestDisplay,
+    TestWindow, WindowAppearance, WindowParams, size,
 };
 use anyhow::Result;
 use collections::VecDeque;
@@ -91,17 +91,7 @@ impl TestPlatform {
             )
         };
 
-        #[cfg(target_os = "macos")]
-        let text_system = Arc::new(crate::platform::mac::MacTextSystem::new());
-
-        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
-        let text_system = Arc::new(crate::platform::linux::CosmicTextSystem::new());
-
-        #[cfg(target_os = "windows")]
-        let text_system = Arc::new(
-            crate::platform::windows::DirectWriteTextSystem::new(&bitmap_factory)
-                .expect("Unable to initialize direct write."),
-        );
+        let text_system = Arc::new(NoopTextSystem);
 
         Rc::new_cyclic(|weak| TestPlatform {
             background_executor: executor,

crates/gpui/src/text_system/line_wrapper.rs 🔗

@@ -330,13 +330,6 @@ mod tests {
     fn build_wrapper() -> LineWrapper {
         let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0));
         let cx = TestAppContext::new(dispatcher, None);
-        cx.text_system()
-            .add_fonts(vec![
-                std::fs::read("../../assets/fonts/plex-mono/ZedPlexMono-Regular.ttf")
-                    .unwrap()
-                    .into(),
-            ])
-            .unwrap();
         let id = cx.text_system().font_id(&font("Zed Plex Mono")).unwrap();
         LineWrapper::new(id, px(16.), cx.text_system().platform_text_system.clone())
     }
@@ -734,16 +727,16 @@ mod tests {
                 lines[0].layout.wrap_boundaries(),
                 &[
                     WrapBoundary {
-                        run_ix: 1,
-                        glyph_ix: 3
+                        run_ix: 0,
+                        glyph_ix: 7
                     },
                     WrapBoundary {
-                        run_ix: 2,
-                        glyph_ix: 3
+                        run_ix: 0,
+                        glyph_ix: 12
                     },
                     WrapBoundary {
-                        run_ix: 4,
-                        glyph_ix: 2
+                        run_ix: 0,
+                        glyph_ix: 18
                     }
                 ],
             );