blob: 52c97c17e69e22c33284b68d701917bd0e2f5475 [file] [log] [blame]
use std::path::{Path, PathBuf};
use span::{self, Column, Position, Row};
use super::{
make_line_indices, Change, Error, File, FileContents, FileKind, FileLoader, TextFile,
VfsInternal, VfsSpan
};
type Span = span::Span<span::ZeroIndexed>;
struct MockFileLoader;
impl FileLoader for MockFileLoader {
fn read<U>(file_name: &Path) -> Result<File<U>, Error> {
let text = format!("{}\nHello\nWorld\nHello, World!\n", file_name.display());
let text_file = TextFile {
line_indices: make_line_indices(&text),
text: text,
changed: false,
};
Ok(File {
kind: FileKind::Text(text_file),
user_data: None,
})
}
fn write(file_name: &Path, file: &FileKind) -> Result<(), Error> {
if let FileKind::Text(ref text_file) = *file {
if file_name.display().to_string() == "foo" {
// TODO: is this test useful still?
assert_eq!(text_file.changed, false);
assert_eq!(text_file.text, "foo\nHfooo\nWorld\nHello, World!\n");
}
}
Ok(())
}
}
fn make_change(with_len: bool) -> Change {
let (row_end, col_end, len) = if with_len {
// If len is present, we shouldn't depend on row_end/col_end
// at all, because they may be invalid.
(0, 0, Some(3))
} else {
(1, 4, None)
};
Change::ReplaceText {
span: VfsSpan::from_usv(
Span::new(
Row::new_zero_indexed(1),
Row::new_zero_indexed(row_end),
Column::new_zero_indexed(1),
Column::new_zero_indexed(col_end),
"foo",
),
len,
),
text: "foo".to_owned(),
}
}
fn make_change_2(with_len: bool) -> Change {
let (row_end, col_end, len) = if with_len {
// If len is present, we shouldn't depend on row_end/col_end
// at all, because they may be invalid.
(0, 0, Some(4))
} else {
(3, 2, None)
};
Change::ReplaceText {
span: VfsSpan::from_usv(
Span::new(
Row::new_zero_indexed(2),
Row::new_zero_indexed(row_end),
Column::new_zero_indexed(4),
Column::new_zero_indexed(col_end),
"foo",
),
len,
),
text: "aye carumba".to_owned(),
}
}
fn test_has_changes(with_len: bool) {
let vfs = VfsInternal::<MockFileLoader, ()>::new();
assert!(!vfs.has_changes());
vfs.load_file(&Path::new("foo")).unwrap();
assert!(!vfs.has_changes());
vfs.on_changes(&[make_change(with_len)]).unwrap();
assert!(vfs.has_changes());
vfs.file_saved(&Path::new("bar")).unwrap();
assert!(vfs.has_changes());
vfs.file_saved(&Path::new("foo")).unwrap();
assert!(!vfs.has_changes());
}
#[test]
fn test_has_changes_without_len() {
test_has_changes(false)
}
#[test]
fn test_has_changes_with_len() {
test_has_changes(true)
}
#[test]
fn test_cached_files() {
let vfs = VfsInternal::<MockFileLoader, ()>::new();
assert!(vfs.get_cached_files().is_empty());
vfs.load_file(&Path::new("foo")).unwrap();
vfs.load_file(&Path::new("bar")).unwrap();
let files = vfs.get_cached_files();
assert!(files.len() == 2);
assert!(files[Path::new("foo")] == "foo\nHello\nWorld\nHello, World!\n");
assert!(files[Path::new("bar")] == "bar\nHello\nWorld\nHello, World!\n");
}
#[test]
fn test_flush_file() {
let vfs = VfsInternal::<MockFileLoader, ()>::new();
// Flushing an uncached-file should succeed.
vfs.flush_file(&Path::new("foo")).unwrap();
vfs.load_file(&Path::new("foo")).unwrap();
vfs.flush_file(&Path::new("foo")).unwrap();
assert!(vfs.get_cached_files().is_empty());
}
fn test_changes(with_len: bool) {
let vfs = VfsInternal::<MockFileLoader, ()>::new();
vfs.on_changes(&[make_change(with_len)]).unwrap();
let files = vfs.get_cached_files();
assert!(files.len() == 1);
assert_eq!(files[&PathBuf::from("foo")], "foo\nHfooo\nWorld\nHello, World!\n");
assert_eq!(
vfs.load_file(&Path::new("foo")).unwrap(),
FileContents::Text("foo\nHfooo\nWorld\nHello, World!\n".to_owned()),
);
assert_eq!(
vfs.load_file(&Path::new("bar")).unwrap(),
FileContents::Text("bar\nHello\nWorld\nHello, World!\n".to_owned()),
);
vfs.on_changes(&[make_change_2(with_len)]).unwrap();
let files = vfs.get_cached_files();
assert!(files.len() == 2);
assert_eq!(files[&PathBuf::from("foo")], "foo\nHfooo\nWorlaye carumballo, World!\n");
assert_eq!(
vfs.load_file(&Path::new("foo")).unwrap(),
FileContents::Text("foo\nHfooo\nWorlaye carumballo, World!\n".to_owned()),
);
}
#[test]
fn test_changes_without_len() {
test_changes(false)
}
#[test]
fn test_changes_with_len() {
test_changes(true)
}
#[test]
fn test_change_add_file() {
let vfs = VfsInternal::<MockFileLoader, ()>::new();
let new_file = Change::AddFile {
file: PathBuf::from("foo"),
text: "Hello, World!".to_owned(),
};
vfs.on_changes(&[new_file]).unwrap();
let files = vfs.get_cached_files();
assert_eq!(files.len(), 1);
assert_eq!(files[&PathBuf::from("foo")], "Hello, World!");
}
fn test_user_data(with_len: bool) {
let vfs = VfsInternal::<MockFileLoader, i32>::new();
// New files have no user data.
vfs.load_file(&Path::new("foo")).unwrap();
vfs.with_user_data(&Path::new("foo"), |u| {
assert_eq!(u, Err(Error::NoUserDataForFile));
Ok(())
}).unwrap();
// Set and read data.
vfs.set_user_data(&Path::new("foo"), Some(42)).unwrap();
vfs.with_user_data(&Path::new("foo"), |u| {
assert_eq!(*u.unwrap().1, 42);
Ok(())
}).unwrap();
assert_eq!(
vfs.set_user_data(&Path::new("bar"), Some(42)),
Err(Error::FileNotCached)
);
// ensure_user_data should not be called if the userdata already exists.
vfs.ensure_user_data(&Path::new("foo"), |_| panic!())
.unwrap();
// Test ensure_user_data is called.
vfs.load_file(&Path::new("bar")).unwrap();
vfs.ensure_user_data(&Path::new("bar"), |_| Ok(1)).unwrap();
vfs.with_user_data(&Path::new("bar"), |u| {
assert_eq!(*u.unwrap().1, 1);
Ok(())
}).unwrap();
// compute and read data.
vfs.with_user_data(&Path::new("foo"), |u| {
assert_eq!(u.as_ref().unwrap().0, Some("foo\nHello\nWorld\nHello, World!\n"));
*u.unwrap().1 = 43;
Ok(())
}).unwrap();
vfs.with_user_data(&Path::new("foo"), |u| {
assert_eq!(*u.unwrap().1, 43);
Ok(())
}).unwrap();
assert_eq!(
vfs.with_user_data(&Path::new("foo"), |u| {
assert_eq!(*u.unwrap().1, 43);
Err(Error::BadLocation): Result<(), Error>
}),
Err(Error::BadLocation)
);
vfs.with_user_data(&Path::new("foo"), |u| {
assert_eq!(*u.unwrap().1, 43);
Ok(())
}).unwrap();
// Clear and read data.
vfs.set_user_data(&Path::new("foo"), None).unwrap();
vfs.with_user_data(&Path::new("foo"), |u| {
assert_eq!(u, Err(Error::NoUserDataForFile));
Ok(())
}).unwrap();
// Compute (clear) and read data.
vfs.set_user_data(&Path::new("foo"), Some(42)).unwrap();
assert_eq!(
vfs.with_user_data(&Path::new("foo"), |_| {
Err(Error::NoUserDataForFile): Result<(), Error>
}),
Err(Error::NoUserDataForFile)
);
vfs.with_user_data(&Path::new("foo"), |u| {
assert_eq!(u, Err(Error::NoUserDataForFile));
Ok(())
}).unwrap();
// Flushing a file should clear user data.
vfs.set_user_data(&Path::new("foo"), Some(42)).unwrap();
vfs.flush_file(&Path::new("foo")).unwrap();
vfs.load_file(&Path::new("foo")).unwrap();
vfs.with_user_data(&Path::new("foo"), |u| {
assert_eq!(u, Err(Error::NoUserDataForFile));
Ok(())
}).unwrap();
// Recording a change should clear user data.
vfs.set_user_data(&Path::new("foo"), Some(42)).unwrap();
vfs.on_changes(&[make_change(with_len)]).unwrap();
vfs.with_user_data(&Path::new("foo"), |u| {
assert_eq!(u, Err(Error::NoUserDataForFile));
Ok(())
}).unwrap();
}
#[test]
fn test_user_data_without_len() {
test_user_data(false)
}
#[test]
fn test_user_data_with_len() {
test_user_data(true)
}
fn test_write(with_len: bool) {
let vfs = VfsInternal::<MockFileLoader, ()>::new();
vfs.on_changes(&[make_change(with_len)]).unwrap();
vfs.write_file(&Path::new("foo")).unwrap();
let files = vfs.get_cached_files();
assert!(files.len() == 1);
let files = vfs.get_changes();
assert!(files.is_empty());
}
#[test]
fn test_write_without_len() {
test_write(false)
}
#[test]
fn test_write_with_len() {
test_write(true)
}
#[test]
fn test_clear() {
let vfs = VfsInternal::<MockFileLoader, ()>::new();
vfs.load_file(&Path::new("foo")).unwrap();
vfs.load_file(&Path::new("bar")).unwrap();
assert!(vfs.get_cached_files().len() == 2);
vfs.clear();
assert!(vfs.get_cached_files().is_empty());
}
#[test]
fn test_wide_utf8() {
let vfs = VfsInternal::<MockFileLoader, ()>::new();
let changes = [
Change::AddFile {
file: PathBuf::from("foo"),
text: String::from("😢"),
},
Change::ReplaceText {
span: VfsSpan::from_usv(
Span::from_positions(
Position::new(Row::new_zero_indexed(0), Column::new_zero_indexed(0)),
Position::new(Row::new_zero_indexed(0), Column::new_zero_indexed(1)),
"foo",
),
Some(1),
),
text: "".into(),
},
];
vfs.on_changes(&changes).unwrap();
assert_eq!(
vfs.load_file(&Path::new("foo")).unwrap(),
FileContents::Text("".to_owned()),
);
}
#[test]
fn test_wide_utf16() {
let vfs = VfsInternal::<MockFileLoader, ()>::new();
let changes = [
Change::AddFile {
file: PathBuf::from("foo"),
text: String::from("😢"),
},
Change::ReplaceText {
span: VfsSpan::from_utf16(
Span::from_positions(
Position::new(Row::new_zero_indexed(0), Column::new_zero_indexed(0)),
Position::new(Row::new_zero_indexed(0), Column::new_zero_indexed(2)),
"foo",
),
Some(2),
),
text: "".into(),
},
];
vfs.on_changes(&changes).unwrap();
assert_eq!(
vfs.load_file(&Path::new("foo")).unwrap(),
FileContents::Text("".to_owned()),
);
}