From c3ed498dc9292880e4ad4c358eea333b07e63a7d Mon Sep 17 00:00:00 2001 From: Ingrid Date: Sun, 22 Sep 2024 13:17:05 +0200 Subject: [PATCH] add integration test for persistent state --- helix-term/tests/integration.rs | 1 + helix-term/tests/test/helpers.rs | 23 +---- helix-term/tests/test/persistence.rs | 142 +++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 22 deletions(-) create mode 100644 helix-term/tests/test/persistence.rs diff --git a/helix-term/tests/integration.rs b/helix-term/tests/integration.rs index 35214bcb8..6d5b3caa7 100644 --- a/helix-term/tests/integration.rs +++ b/helix-term/tests/integration.rs @@ -20,6 +20,7 @@ mod test { mod commands; mod languages; mod movement; + mod persistence; mod prompt; mod splits; } diff --git a/helix-term/tests/test/helpers.rs b/helix-term/tests/test/helpers.rs index 623f5df3e..be39caedd 100644 --- a/helix-term/tests/test/helpers.rs +++ b/helix-term/tests/test/helpers.rs @@ -11,7 +11,7 @@ use helix_core::{diagnostic::Severity, test, Selection, Transaction}; use helix_loader; use helix_term::{application::Application, args::Args, config::Config, keymap::merge_keys}; use helix_view::{current_ref, doc, editor::LspConfig, input::parse_macro, Editor}; -use tempfile::{NamedTempFile, TempPath}; +use tempfile::NamedTempFile; use tokio_stream::wrappers::UnboundedReceiverStream; /// Specify how to set up the input text with line feeds @@ -230,26 +230,6 @@ pub fn test_syntax_loader(overrides: Option) -> helix_core::syntax::Load helix_core::syntax::Loader::new(lang.try_into().unwrap()).unwrap() } -fn init_persistence_files() -> anyhow::Result<(TempPath, TempPath, TempPath, TempPath)> { - let command_file = NamedTempFile::new()?; - let command_path = command_file.into_temp_path(); - helix_loader::initialize_command_histfile(Some(command_path.to_path_buf())); - - let search_file = NamedTempFile::new()?; - let search_path = search_file.into_temp_path(); - helix_loader::initialize_search_histfile(Some(search_path.to_path_buf())); - - let file_file = NamedTempFile::new()?; - let file_path = file_file.into_temp_path(); - helix_loader::initialize_file_histfile(Some(file_path.to_path_buf())); - - let clipboard_file = NamedTempFile::new()?; - let clipboard_path = clipboard_file.into_temp_path(); - helix_loader::initialize_clipboard_file(Some(clipboard_path.to_path_buf())); - - Ok((command_path, search_path, file_path, clipboard_path)) -} - /// Use this for very simple test cases where there is one input /// document, selection, and sequence of key presses, and you just /// want to verify the resulting document and selection. @@ -257,7 +237,6 @@ pub async fn test_with_config>( app_builder: AppBuilder, test_case: T, ) -> anyhow::Result<()> { - let (_, _, _, _) = init_persistence_files()?; let test_case = test_case.into(); let app = app_builder.build()?; diff --git a/helix-term/tests/test/persistence.rs b/helix-term/tests/test/persistence.rs new file mode 100644 index 000000000..34bacb426 --- /dev/null +++ b/helix-term/tests/test/persistence.rs @@ -0,0 +1,142 @@ +use super::*; +use helix_term::{config::Config, keymap}; +use helix_view::editor; +use std::{fs::File, io::Read}; +use tempfile::{NamedTempFile, TempPath}; + +fn init_persistence_files() -> anyhow::Result<(TempPath, TempPath, TempPath, TempPath)> { + let command_file = NamedTempFile::new()?; + let command_path = command_file.into_temp_path(); + helix_loader::initialize_command_histfile(Some(command_path.to_path_buf())); + + let search_file = NamedTempFile::new()?; + let search_path = search_file.into_temp_path(); + helix_loader::initialize_search_histfile(Some(search_path.to_path_buf())); + + let file_file = NamedTempFile::new()?; + let file_path = file_file.into_temp_path(); + helix_loader::initialize_file_histfile(Some(file_path.to_path_buf())); + + let clipboard_file = NamedTempFile::new()?; + let clipboard_path = clipboard_file.into_temp_path(); + helix_loader::initialize_clipboard_file(Some(clipboard_path.to_path_buf())); + + Ok((command_path, search_path, file_path, clipboard_path)) +} + +fn config_with_persistence() -> Config { + let mut editor_config = editor::Config::default(); + editor_config.persistence.old_files = true; + editor_config.persistence.commands = true; + editor_config.persistence.search = true; + editor_config.persistence.clipboard = true; + editor_config.persistence.search_trim = 3; + + Config { + theme: None, + keys: keymap::default(), + editor: editor_config, + } +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_persistence() -> anyhow::Result<()> { + let (_, search_histfile_path, _, _) = init_persistence_files()?; + let mut file = tempfile::NamedTempFile::new()?; + + // Session 1: + // open a new file, + // add a newline, then a, + // write-quit + test_key_sequence( + &mut helpers::AppBuilder::new() + .with_config(config_with_persistence()) + .with_file(file.path(), None) + .build()?, + // TODO: remove the h with a bugfix? + Some("oah:wq"), + // Some(&|app| { + // assert!(!app.editor.is_err(), "error: {:?}", app.editor.get_status()); + // }), + None, + true, + ) + .await?; + + // Sanity check contents of file after first session + helpers::assert_file_has_content(&mut file, "\na\n")?; + + // Session 2: + // open same file, + // add newline, then b, + // copy the line + // search for "a" + // go back down to b + // use last command + test_key_sequence( + &mut helpers::AppBuilder::new() + .with_config(config_with_persistence()) + .with_file(file.path(), None) + .build()?, + Some("obxy/aj:"), + None, + true, + ) + .await?; + + // This verifies both that the file position was persisted (since the b is inserted after the + // a), and the command history (":" resolves to the ":wq" from session 1) + helpers::assert_file_has_content(&mut file, "\na\nb\n")?; + + // Session 3: + // open same file, + // paste + // use last search + // append a + // search for "1", "2", and "3" in sequence. + // use last command + test_key_sequence( + &mut helpers::AppBuilder::new() + .with_config(config_with_persistence()) + .with_file(file.path(), None) + .build()?, + Some("p/aa/1/2/3:"), + None, + true, + ) + .await?; + + // This verifies search history was persisted ("/" resolves to "/a" from session 2), and + // the clipboard was persisted (paste pastes the "b\n" copied in session 2) + helpers::assert_file_has_content(&mut file, "\naa\nb\nb\n")?; + + // Session 4: + // open same file + // use last command + test_key_sequence( + &mut helpers::AppBuilder::new() + .with_config(config_with_persistence()) + .with_file(file.path(), None) + .build()?, + Some(":"), + None, + true, + ) + .await?; + + // NOTE: This time we check the search history file, instead of the edited file + let mut search_histfile = File::open(search_histfile_path)?; + let mut search_histfile_contents = String::new(); + search_histfile.read_to_string(&mut search_histfile_contents)?; + // This verifies that trimming the persistent state files is working correctly, because + // session 3 sent more searches (4: "/a", "/1", "/2", "/3") than the trim limit (3), so when + // session 4 starts, it should perform a trim, removing the oldest entry ("/a") while leaving + // the other 3 intact. + // The weird looking format of the string is because persistence data is encoded using bincode. + assert_eq!( + search_histfile_contents, + "\u{1}\0\0\0\0\0\0\01\u{1}\0\0\0\0\0\0\02\u{1}\0\0\0\0\0\0\03" + ); + + Ok(()) +}