terminal_test_context.rs

  1use std::{path::Path, time::Duration};
  2
  3use gpui::{
  4    geometry::vector::vec2f, AppContext, ModelHandle, ReadModelWith, TestAppContext, ViewHandle,
  5};
  6use itertools::Itertools;
  7use project::{Entry, Project, ProjectPath, Worktree};
  8use workspace::{AppState, Workspace};
  9
 10use crate::{
 11    connected_el::TermDimensions,
 12    model::{Terminal, TerminalBuilder},
 13    DEBUG_CELL_WIDTH, DEBUG_LINE_HEIGHT, DEBUG_TERMINAL_HEIGHT, DEBUG_TERMINAL_WIDTH,
 14};
 15
 16pub struct TerminalTestContext<'a> {
 17    pub cx: &'a mut TestAppContext,
 18    pub connection: Option<ModelHandle<Terminal>>,
 19}
 20
 21impl<'a> TerminalTestContext<'a> {
 22    pub fn new(cx: &'a mut TestAppContext, term: bool) -> Self {
 23        cx.set_condition_duration(Some(Duration::from_secs(5)));
 24
 25        let size_info = TermDimensions::new(
 26            DEBUG_CELL_WIDTH,
 27            DEBUG_LINE_HEIGHT,
 28            vec2f(DEBUG_TERMINAL_WIDTH, DEBUG_TERMINAL_HEIGHT),
 29        );
 30
 31        let connection = term.then(|| {
 32            cx.add_model(|cx| {
 33                TerminalBuilder::new(None, None, None, size_info)
 34                    .unwrap()
 35                    .subscribe(cx)
 36            })
 37        });
 38
 39        TerminalTestContext { cx, connection }
 40    }
 41
 42    pub async fn execute_and_wait<F>(&mut self, command: &str, f: F) -> String
 43    where
 44        F: Fn(String, &AppContext) -> bool,
 45    {
 46        let connection = self.connection.take().unwrap();
 47
 48        let command = command.to_string();
 49        connection.update(self.cx, |connection, _| {
 50            connection.write_to_pty(command);
 51            connection.write_to_pty("\r".to_string());
 52        });
 53
 54        connection
 55            .condition(self.cx, |conn, cx| {
 56                let content = Self::grid_as_str(conn);
 57                f(content, cx)
 58            })
 59            .await;
 60
 61        let res = self
 62            .cx
 63            .read_model_with(&connection, &mut |conn, _: &AppContext| {
 64                Self::grid_as_str(conn)
 65            });
 66
 67        self.connection = Some(connection);
 68
 69        res
 70    }
 71
 72    ///Creates a worktree with 1 file: /root.txt
 73    pub async fn blank_workspace(&mut self) -> (ModelHandle<Project>, ViewHandle<Workspace>) {
 74        let params = self.cx.update(AppState::test);
 75
 76        let project = Project::test(params.fs.clone(), [], self.cx).await;
 77        let (_, workspace) = self.cx.add_window(|cx| Workspace::new(project.clone(), cx));
 78
 79        (project, workspace)
 80    }
 81
 82    ///Creates a worktree with 1 folder: /root{suffix}/
 83    pub async fn create_folder_wt(
 84        &mut self,
 85        project: ModelHandle<Project>,
 86        path: impl AsRef<Path>,
 87    ) -> (ModelHandle<Worktree>, Entry) {
 88        self.create_wt(project, true, path).await
 89    }
 90
 91    ///Creates a worktree with 1 file: /root{suffix}.txt
 92    pub async fn create_file_wt(
 93        &mut self,
 94        project: ModelHandle<Project>,
 95        path: impl AsRef<Path>,
 96    ) -> (ModelHandle<Worktree>, Entry) {
 97        self.create_wt(project, false, path).await
 98    }
 99
100    async fn create_wt(
101        &mut self,
102        project: ModelHandle<Project>,
103        is_dir: bool,
104        path: impl AsRef<Path>,
105    ) -> (ModelHandle<Worktree>, Entry) {
106        let (wt, _) = project
107            .update(self.cx, |project, cx| {
108                project.find_or_create_local_worktree(path, true, cx)
109            })
110            .await
111            .unwrap();
112
113        let entry = self
114            .cx
115            .update(|cx| {
116                wt.update(cx, |wt, cx| {
117                    wt.as_local()
118                        .unwrap()
119                        .create_entry(Path::new(""), is_dir, cx)
120                })
121            })
122            .await
123            .unwrap();
124
125        (wt, entry)
126    }
127
128    pub fn insert_active_entry_for(
129        &mut self,
130        wt: ModelHandle<Worktree>,
131        entry: Entry,
132        project: ModelHandle<Project>,
133    ) {
134        self.cx.update(|cx| {
135            let p = ProjectPath {
136                worktree_id: wt.read(cx).id(),
137                path: entry.path,
138            };
139            project.update(cx, |project, cx| project.set_active_path(Some(p), cx));
140        });
141    }
142
143    fn grid_as_str(connection: &Terminal) -> String {
144        connection.render_lock(None, |content, _| {
145            let lines = content.display_iter.group_by(|i| i.point.line.0);
146            lines
147                .into_iter()
148                .map(|(_, line)| line.map(|i| i.c).collect::<String>())
149                .collect::<Vec<String>>()
150                .join("\n")
151        })
152    }
153}
154
155impl<'a> Drop for TerminalTestContext<'a> {
156    fn drop(&mut self) {
157        self.cx.set_condition_duration(None);
158    }
159}