Skip to content

Commit

Permalink
write reload unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
kirawi committed Feb 20, 2023
1 parent 91def8e commit 02f4d8d
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 8 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 18 additions & 5 deletions helix-core/src/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub struct State {
pub struct History {
revisions: Vec<Revision>,
current: usize,
version: Option<NonZeroUsize>,
}

/// A single point in history. See [History] for more information.
Expand Down Expand Up @@ -90,6 +91,7 @@ impl Default for History {
timestamp: Instant::now(),
}],
current: 0,
version: None,
}
}
}
Expand Down Expand Up @@ -118,7 +120,7 @@ impl Revision {
}

// Temporarily 3 for review.
const HEADER_TAG: &str = "Helix Undofile 3\n";
const HEADER_TAG: &str = "Helix Undofile 4\n";

fn get_hash<R: Read>(reader: &mut R) -> std::io::Result<[u8; 20]> {
const BUF_SIZE: usize = 8192;
Expand Down Expand Up @@ -149,12 +151,15 @@ impl History {
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let version = self.version.map(|n| n.get()).unwrap_or(0);
write_string(writer, HEADER_TAG)?;
write_usize(writer, self.current)?;
write_usize(writer, revision)?;
write_u64(writer, mtime)?;
write_usize(writer, version)?;
writer.write_all(&get_hash(&mut std::fs::File::open(path)?)?)?;

// Append new revisions to the end of the file.
write_usize(writer, self.revisions.len())?;
writer.seek(SeekFrom::End(0))?;
for rev in &self.revisions[last_saved_revision..] {
Expand All @@ -164,7 +169,7 @@ impl History {
}

pub fn deserialize<R: Read>(reader: &mut R, path: &Path) -> std::io::Result<(usize, Self)> {
let (current, last_saved_revision) = Self::read_header(reader, path)?;
let (current, last_saved_revision, version) = Self::read_header(reader, path)?;
let timestamp = Instant::now();
let len = read_usize(reader)?;
let mut revisions: Vec<Revision> = Vec::with_capacity(len);
Expand All @@ -179,7 +184,11 @@ impl History {
revisions.push(res);
}

let history = History { current, revisions };
let history = History {
current,
revisions,
version,
};
Ok((last_saved_revision, history))
}

Expand All @@ -200,7 +209,10 @@ impl History {
Ok(())
}

pub fn read_header<R: Read>(reader: &mut R, path: &Path) -> std::io::Result<(usize, usize)> {
pub fn read_header<R: Read>(
reader: &mut R,
path: &Path,
) -> std::io::Result<(usize, usize, Option<NonZeroUsize>)> {
let header = read_string(reader)?;
if HEADER_TAG != header {
Err(std::io::Error::new(
Expand All @@ -216,6 +228,7 @@ impl History {
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let version = NonZeroUsize::new(read_usize(reader)?);
let mut hash = [0u8; 20];
reader.read_exact(&mut hash)?;

Expand All @@ -225,7 +238,7 @@ impl History {
"outdated undo file",
));
}
Ok((current, last_saved_revision))
Ok((current, last_saved_revision, version))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion helix-loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub fn local_config_dirs() -> Vec<PathBuf> {
}

pub fn cache_dir() -> PathBuf {
let mut path = if cfg!(feature = "integration") {
let mut path = if cfg!(feature = "integration") || cfg!(test) {
std::env::temp_dir()
} else {
// TODO: allow env var override
Expand Down
2 changes: 2 additions & 0 deletions helix-view/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,6 @@ clipboard-win = { version = "4.5", features = ["std"] }
libc = "0.2"

[dev-dependencies]
quickcheck = { version = "1", default-features = false }
tempfile = "3"
helix-tui = { path = "../helix-tui" }
96 changes: 95 additions & 1 deletion helix-view/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ impl Document {
.into_std()
.await;
tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
// Truncate the file if it's not a valid undofile.
if History::deserialize(&mut std::fs::File::open(&undofile_path)?, &path)
.is_ok()
{
Expand Down Expand Up @@ -1386,7 +1387,8 @@ impl Display for FormatterError {

#[cfg(test)]
mod test {
use arc_swap::ArcSwap;
use arc_swap::{access::Map, ArcSwap};
use quickcheck::Gen;

use super::*;

Expand Down Expand Up @@ -1556,6 +1558,98 @@ mod test {
);
}

#[tokio::test(flavor = "multi_thread")]
async fn reload_history() {
let test_fn: fn(Vec<String>) -> bool = |changes| -> bool {
let len = changes.len() / 3;
let mut original = Rope::new();
let mut iter = changes.into_iter();

let changes_a: Vec<_> = iter
.by_ref()
.take(len)
.map(|c| {
let c = Rope::from(c);
let transaction = helix_core::diff::compare_ropes(&original, &c);
original = c;
transaction
})
.collect();
let mut original_concurrent = original.clone();

let changes_b: Vec<_> = iter
.by_ref()
.take(len)
.map(|c| {
let c = Rope::from(c);
let transaction = helix_core::diff::compare_ropes(&original, &c);
original = c;
transaction
})
.collect();
let changes_c: Vec<_> = iter
.take(len)
.map(|c| {
let c = Rope::from(c);
let transaction = helix_core::diff::compare_ropes(&original_concurrent, &c);
original_concurrent = c;
transaction
})
.collect();

let file = tempfile::NamedTempFile::new().unwrap();
let mut config = Config::default();
config.persistent_undo = true;

let view_id = ViewId::default();
let config = Arc::new(ArcSwap::new(Arc::new(config)));
let mut doc_1 = Document::open(file.path(), None, None, config.clone()).unwrap();
doc_1.ensure_view_init(view_id);

// Make changes & save document A
for c in changes_a {
doc_1.apply(&c, view_id);
}
helix_lsp::block_on(doc_1.save::<PathBuf>(None, true).unwrap()).unwrap();

let mut doc_2 = Document::open(file.path(), None, None, config.clone()).unwrap();
let mut doc_3 = Document::open(file.path(), None, None, config.clone()).unwrap();
doc_2.ensure_view_init(view_id);
doc_3.ensure_view_init(view_id);

// Make changes in A and B at the same time.
for c in changes_b {
doc_1.apply(&c, view_id);
}

for c in changes_c {
doc_2.apply(&c, view_id);
}
helix_lsp::block_on(doc_2.save::<PathBuf>(None, true).unwrap()).unwrap();

doc_1.load_history().unwrap();
doc_3.load_history().unwrap();

assert_eq!(doc_2.history.get_mut(), doc_3.history.get_mut());

helix_lsp::block_on(doc_1.save::<PathBuf>(None, true).unwrap()).unwrap();
doc_2.load_history().unwrap();
doc_3.load_history().unwrap();
doc_1.history.get_mut() == doc_2.history.get_mut()
&& doc_1.history.get_mut() == doc_3.history.get_mut()
};
let handles: Vec<_> = (0..100)
.map(|_| {
tokio::task::spawn_blocking(move || {
quickcheck::QuickCheck::new()
.max_tests(1)
.quickcheck(test_fn);
})
})
.collect();
futures_util::future::try_join_all(handles).await.unwrap();
}

macro_rules! decode {
($name:ident, $label:expr, $label_override:expr) => {
#[test]
Expand Down

0 comments on commit 02f4d8d

Please sign in to comment.