Rebrand to Galaxy, major improvements to Bedrock support, still needs some TLC though
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
use async_channel::{unbounded, Receiver};
|
||||
use galaxyui::{r#async::block_on, App, ModelHandle};
|
||||
|
||||
// lib_tests.rs
|
||||
use super::*;
|
||||
|
||||
const WRITE_TEST_PATH: &str = "test_data/test_write/";
|
||||
|
||||
/// This enum is used so that we can pass the event through the async channel.
|
||||
/// io::Error is not clonable, so we can't clone the FileModelEvent.
|
||||
#[derive(Debug)]
|
||||
enum TestFileModelEvent {
|
||||
FileLoaded {
|
||||
id: FileId,
|
||||
content: String,
|
||||
_version: ContentVersion,
|
||||
},
|
||||
FileSaved,
|
||||
FailedToLoad(String),
|
||||
FailedToSave,
|
||||
}
|
||||
|
||||
impl From<&FileModelEvent> for TestFileModelEvent {
|
||||
fn from(event: &FileModelEvent) -> Self {
|
||||
match event {
|
||||
FileModelEvent::FileLoaded {
|
||||
id,
|
||||
content,
|
||||
version,
|
||||
} => TestFileModelEvent::FileLoaded {
|
||||
id: *id,
|
||||
content: content.clone(),
|
||||
_version: *version,
|
||||
},
|
||||
FileModelEvent::FileSaved { .. } => TestFileModelEvent::FileSaved,
|
||||
FileModelEvent::FailedToLoad {
|
||||
id: _id,
|
||||
error: err,
|
||||
} => TestFileModelEvent::FailedToLoad(format!("{err:?}")),
|
||||
FileModelEvent::FailedToSave { .. } => TestFileModelEvent::FailedToSave,
|
||||
FileModelEvent::FileUpdated { .. } => {
|
||||
// For now, we don't handle file updated events in tests
|
||||
// This could be extended to include a FileUpdated variant in TestFileModelEvent if needed
|
||||
TestFileModelEvent::FileLoaded {
|
||||
id: event.file_id(),
|
||||
content: String::new(),
|
||||
_version: ContentVersion::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup a Tokio channel that will forward any events from the FileModel to the receiver.
|
||||
fn setup_event_channel(
|
||||
app: &mut App,
|
||||
files: &ModelHandle<FileModel>,
|
||||
) -> Receiver<TestFileModelEvent> {
|
||||
let (sender, receiver) = unbounded();
|
||||
app.update(|ctx| {
|
||||
ctx.subscribe_to_model(files, move |_model, event, _ctx| {
|
||||
block_on(sender.send(TestFileModelEvent::from(event)))
|
||||
.expect("Could not send the result");
|
||||
});
|
||||
});
|
||||
receiver
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load() {
|
||||
App::test((), |mut app| async move {
|
||||
let app = &mut app;
|
||||
let files = app.add_singleton_model(FileModel::new);
|
||||
let receiver = setup_event_channel(app, &files);
|
||||
|
||||
// Load the test file.
|
||||
files.update(app, |model, ctx| {
|
||||
model.open(Path::new("test_data/test_file.rs"), false, ctx);
|
||||
});
|
||||
|
||||
// Check that the first event out is the file loaded event.
|
||||
let event = receiver.recv().await.expect("Could not receive the result");
|
||||
match event {
|
||||
TestFileModelEvent::FileLoaded { content, .. } => {
|
||||
assert_eq!(content.as_bytes(), TEST_FILE_CONTENT)
|
||||
}
|
||||
_ => panic!("Failed to load file"),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save_uninitialized_file() {
|
||||
App::test((), |mut app| async move {
|
||||
let app = &mut app;
|
||||
|
||||
let files = app.add_singleton_model(FileModel::new);
|
||||
let id = FileId::new();
|
||||
|
||||
// This file has not been initialized with the model. Make sure trying to save it fails immediately.
|
||||
files.update(app, |model, ctx| {
|
||||
let result = model.save(
|
||||
id,
|
||||
"This file doesn't exist".to_string(),
|
||||
ContentVersion::new(),
|
||||
ctx,
|
||||
);
|
||||
assert!(result.is_err());
|
||||
|
||||
let e = result.unwrap_err();
|
||||
assert!(matches!(e, FileSaveError::NoFilePath(file_id) if file_id == id));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save_file() {
|
||||
// Create the test write directory if it doesn't exist.
|
||||
std::fs::create_dir_all(WRITE_TEST_PATH).unwrap();
|
||||
|
||||
// Write the test file content to a random file in the test write directory.
|
||||
let path = PathBuf::from(WRITE_TEST_PATH).join("test_save_file.rs");
|
||||
std::fs::write(&path, TEST_FILE_CONTENT).unwrap();
|
||||
|
||||
App::test((), |mut app| async move {
|
||||
let app = &mut app;
|
||||
let files = app.add_singleton_model(FileModel::new);
|
||||
let receiver = setup_event_channel(app, &files);
|
||||
|
||||
// Open the newly created file.
|
||||
let path_clone = path.clone();
|
||||
files.update(app, |model, ctx| {
|
||||
model.open(&path_clone, false, ctx);
|
||||
});
|
||||
|
||||
let file_id = match receiver.recv().await.expect("Could not receive the result") {
|
||||
TestFileModelEvent::FileLoaded { id, .. } => id,
|
||||
_ => panic!("Failed to load file"),
|
||||
};
|
||||
|
||||
let old_version = files.read(app, |files, _ctx| files.version(file_id));
|
||||
let new_version = ContentVersion::new();
|
||||
|
||||
// Save new content to the file.
|
||||
files.update(app, |model, ctx| {
|
||||
let result = model.save(file_id, "Overwrite content".to_string(), new_version, ctx);
|
||||
assert!(result.is_ok());
|
||||
});
|
||||
|
||||
// Make sure that the file saved event was emitted.
|
||||
match receiver.recv().await.expect("Could not receive the result") {
|
||||
TestFileModelEvent::FileSaved => (),
|
||||
_ => panic!("Failed to save file"),
|
||||
}
|
||||
|
||||
// Make sure the content on disk matches the content we saved.
|
||||
let content = std::fs::read_to_string(&path).unwrap();
|
||||
assert_eq!(content, "Overwrite content");
|
||||
|
||||
// Make sure the version was updated.
|
||||
let model_version = files.read(app, |files, _ctx| files.version(file_id));
|
||||
assert_ne!(old_version, model_version);
|
||||
assert_eq!(Some(new_version), model_version);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_missing_file() {
|
||||
App::test((), |mut app| async move {
|
||||
let app = &mut app;
|
||||
let files = app.add_singleton_model(FileModel::new);
|
||||
let receiver = setup_event_channel(app, &files);
|
||||
|
||||
// Load a file that doesn't exist.
|
||||
files.update(app, |model, ctx| {
|
||||
model.open(Path::new("test_data/missing_file.rs"), false, ctx);
|
||||
});
|
||||
|
||||
// Check that the first event out is the failed to load event.
|
||||
let event = receiver.recv().await.expect("Could not receive the result");
|
||||
match event {
|
||||
TestFileModelEvent::FailedToLoad(err) => {
|
||||
// File not found error strings differ across operating systems.
|
||||
#[cfg(not(windows))]
|
||||
let os_error_message = "No such file or directory";
|
||||
#[cfg(windows)]
|
||||
let os_error_message = "The system cannot find the file specified.";
|
||||
|
||||
assert_eq!(
|
||||
err,
|
||||
format!(
|
||||
"IOError(Os {{ code: 2, kind: NotFound, message: \"{os_error_message}\" }})"
|
||||
)
|
||||
);
|
||||
}
|
||||
_ => panic!("Failed to load file"),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save_missing_directory() {
|
||||
// Create the test write directory if it doesn't exist.
|
||||
let directory = PathBuf::from(WRITE_TEST_PATH).join("missing-directory");
|
||||
std::fs::create_dir_all(&directory).unwrap();
|
||||
|
||||
// Write the test file content to a random file in the test write directory.
|
||||
let path = directory.join("test_save_missing_directory.rs");
|
||||
std::fs::write(&path, TEST_FILE_CONTENT).unwrap();
|
||||
|
||||
App::test((), |mut app| async move {
|
||||
let app = &mut app;
|
||||
let files = app.add_singleton_model(FileModel::new);
|
||||
let receiver = setup_event_channel(app, &files);
|
||||
|
||||
// Save a file to a directory that doesn't exist.
|
||||
let file_id = files.update(app, |model, ctx| model.open(&path, false, ctx));
|
||||
|
||||
// Check that the first event out is the successful load.
|
||||
let event = receiver.recv().await.expect("Could not receive the result");
|
||||
match event {
|
||||
TestFileModelEvent::FileLoaded { content, .. } => {
|
||||
assert_eq!(content.as_bytes(), TEST_FILE_CONTENT)
|
||||
}
|
||||
event => panic!("Failed to load file {event:?}"),
|
||||
}
|
||||
|
||||
// Delete the directory that the file is in.
|
||||
std::fs::remove_dir_all(directory).unwrap();
|
||||
|
||||
// Save new content to the file.
|
||||
files.update(app, |model, ctx| {
|
||||
let result = model.save(
|
||||
file_id,
|
||||
"Overwrite content".to_string(),
|
||||
ContentVersion::new(),
|
||||
ctx,
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
});
|
||||
|
||||
// Now we expect the save to succeed because ensure_parent_directories will create the missing directory
|
||||
match receiver.recv().await.expect("Could not receive the result") {
|
||||
TestFileModelEvent::FileSaved => {
|
||||
// Make sure the content on disk matches the content we saved.
|
||||
let content = std::fs::read_to_string(&path).unwrap();
|
||||
assert_eq!(content, "Overwrite content");
|
||||
}
|
||||
event => panic!("Save should have succeeded but got event: {event:?}"),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static TEST_FILE_CONTENT: &[u8] = include_bytes!("../test_data/test_file.rs");
|
||||
Reference in New Issue
Block a user