Skip to content

Commit 02f4d8d

Browse files
committed
write reload unit test
1 parent 91def8e commit 02f4d8d

File tree

5 files changed

+118
-8
lines changed

5 files changed

+118
-8
lines changed

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

helix-core/src/history.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub struct State {
5555
pub struct History {
5656
revisions: Vec<Revision>,
5757
current: usize,
58+
version: Option<NonZeroUsize>,
5859
}
5960

6061
/// A single point in history. See [History] for more information.
@@ -90,6 +91,7 @@ impl Default for History {
9091
timestamp: Instant::now(),
9192
}],
9293
current: 0,
94+
version: None,
9395
}
9496
}
9597
}
@@ -118,7 +120,7 @@ impl Revision {
118120
}
119121

120122
// Temporarily 3 for review.
121-
const HEADER_TAG: &str = "Helix Undofile 3\n";
123+
const HEADER_TAG: &str = "Helix Undofile 4\n";
122124

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

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

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

182-
let history = History { current, revisions };
187+
let history = History {
188+
current,
189+
revisions,
190+
version,
191+
};
183192
Ok((last_saved_revision, history))
184193
}
185194

@@ -200,7 +209,10 @@ impl History {
200209
Ok(())
201210
}
202211

203-
pub fn read_header<R: Read>(reader: &mut R, path: &Path) -> std::io::Result<(usize, usize)> {
212+
pub fn read_header<R: Read>(
213+
reader: &mut R,
214+
path: &Path,
215+
) -> std::io::Result<(usize, usize, Option<NonZeroUsize>)> {
204216
let header = read_string(reader)?;
205217
if HEADER_TAG != header {
206218
Err(std::io::Error::new(
@@ -216,6 +228,7 @@ impl History {
216228
.duration_since(std::time::UNIX_EPOCH)
217229
.unwrap()
218230
.as_secs();
231+
let version = NonZeroUsize::new(read_usize(reader)?);
219232
let mut hash = [0u8; 20];
220233
reader.read_exact(&mut hash)?;
221234

@@ -225,7 +238,7 @@ impl History {
225238
"outdated undo file",
226239
));
227240
}
228-
Ok((current, last_saved_revision))
241+
Ok((current, last_saved_revision, version))
229242
}
230243
}
231244
}

helix-loader/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub fn local_config_dirs() -> Vec<PathBuf> {
7070
}
7171

7272
pub fn cache_dir() -> PathBuf {
73-
let mut path = if cfg!(feature = "integration") {
73+
let mut path = if cfg!(feature = "integration") || cfg!(test) {
7474
std::env::temp_dir()
7575
} else {
7676
// TODO: allow env var override

helix-view/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,6 @@ clipboard-win = { version = "4.5", features = ["std"] }
5252
libc = "0.2"
5353

5454
[dev-dependencies]
55+
quickcheck = { version = "1", default-features = false }
56+
tempfile = "3"
5557
helix-tui = { path = "../helix-tui" }

helix-view/src/document.rs

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ impl Document {
636636
.into_std()
637637
.await;
638638
tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
639+
// Truncate the file if it's not a valid undofile.
639640
if History::deserialize(&mut std::fs::File::open(&undofile_path)?, &path)
640641
.is_ok()
641642
{
@@ -1386,7 +1387,8 @@ impl Display for FormatterError {
13861387

13871388
#[cfg(test)]
13881389
mod test {
1389-
use arc_swap::ArcSwap;
1390+
use arc_swap::{access::Map, ArcSwap};
1391+
use quickcheck::Gen;
13901392

13911393
use super::*;
13921394

@@ -1556,6 +1558,98 @@ mod test {
15561558
);
15571559
}
15581560

1561+
#[tokio::test(flavor = "multi_thread")]
1562+
async fn reload_history() {
1563+
let test_fn: fn(Vec<String>) -> bool = |changes| -> bool {
1564+
let len = changes.len() / 3;
1565+
let mut original = Rope::new();
1566+
let mut iter = changes.into_iter();
1567+
1568+
let changes_a: Vec<_> = iter
1569+
.by_ref()
1570+
.take(len)
1571+
.map(|c| {
1572+
let c = Rope::from(c);
1573+
let transaction = helix_core::diff::compare_ropes(&original, &c);
1574+
original = c;
1575+
transaction
1576+
})
1577+
.collect();
1578+
let mut original_concurrent = original.clone();
1579+
1580+
let changes_b: Vec<_> = iter
1581+
.by_ref()
1582+
.take(len)
1583+
.map(|c| {
1584+
let c = Rope::from(c);
1585+
let transaction = helix_core::diff::compare_ropes(&original, &c);
1586+
original = c;
1587+
transaction
1588+
})
1589+
.collect();
1590+
let changes_c: Vec<_> = iter
1591+
.take(len)
1592+
.map(|c| {
1593+
let c = Rope::from(c);
1594+
let transaction = helix_core::diff::compare_ropes(&original_concurrent, &c);
1595+
original_concurrent = c;
1596+
transaction
1597+
})
1598+
.collect();
1599+
1600+
let file = tempfile::NamedTempFile::new().unwrap();
1601+
let mut config = Config::default();
1602+
config.persistent_undo = true;
1603+
1604+
let view_id = ViewId::default();
1605+
let config = Arc::new(ArcSwap::new(Arc::new(config)));
1606+
let mut doc_1 = Document::open(file.path(), None, None, config.clone()).unwrap();
1607+
doc_1.ensure_view_init(view_id);
1608+
1609+
// Make changes & save document A
1610+
for c in changes_a {
1611+
doc_1.apply(&c, view_id);
1612+
}
1613+
helix_lsp::block_on(doc_1.save::<PathBuf>(None, true).unwrap()).unwrap();
1614+
1615+
let mut doc_2 = Document::open(file.path(), None, None, config.clone()).unwrap();
1616+
let mut doc_3 = Document::open(file.path(), None, None, config.clone()).unwrap();
1617+
doc_2.ensure_view_init(view_id);
1618+
doc_3.ensure_view_init(view_id);
1619+
1620+
// Make changes in A and B at the same time.
1621+
for c in changes_b {
1622+
doc_1.apply(&c, view_id);
1623+
}
1624+
1625+
for c in changes_c {
1626+
doc_2.apply(&c, view_id);
1627+
}
1628+
helix_lsp::block_on(doc_2.save::<PathBuf>(None, true).unwrap()).unwrap();
1629+
1630+
doc_1.load_history().unwrap();
1631+
doc_3.load_history().unwrap();
1632+
1633+
assert_eq!(doc_2.history.get_mut(), doc_3.history.get_mut());
1634+
1635+
helix_lsp::block_on(doc_1.save::<PathBuf>(None, true).unwrap()).unwrap();
1636+
doc_2.load_history().unwrap();
1637+
doc_3.load_history().unwrap();
1638+
doc_1.history.get_mut() == doc_2.history.get_mut()
1639+
&& doc_1.history.get_mut() == doc_3.history.get_mut()
1640+
};
1641+
let handles: Vec<_> = (0..100)
1642+
.map(|_| {
1643+
tokio::task::spawn_blocking(move || {
1644+
quickcheck::QuickCheck::new()
1645+
.max_tests(1)
1646+
.quickcheck(test_fn);
1647+
})
1648+
})
1649+
.collect();
1650+
futures_util::future::try_join_all(handles).await.unwrap();
1651+
}
1652+
15591653
macro_rules! decode {
15601654
($name:ident, $label:expr, $label_override:expr) => {
15611655
#[test]

0 commit comments

Comments
 (0)