use errors::AppError;
use ffi::nfs::*;
use ffi::object_cache::FileContextHandle;
use ffi_utils::test_utils::{call_0, call_1, call_2, call_vec_u8};
use ffi_utils::ErrorCode;
use futures::Future;
use safe_core::ffi::nfs::File;
use safe_core::ffi::MDataInfo;
use safe_core::ipc::Permission;
use safe_core::nfs::File as NativeFile;
use safe_core::nfs::NfsError;
use std;
use std::collections::HashMap;
use std::ffi::CString;
use test_utils::{create_app_by_req, create_auth_req_with_access, run};
use App;
fn setup() -> (App, MDataInfo) {
let mut container_permissions = HashMap::new();
let _ = container_permissions.insert(
"_videos".to_string(),
btree_set![
Permission::Read,
Permission::Insert,
Permission::Update,
Permission::Delete,
],
);
let app = unwrap!(create_app_by_req(&create_auth_req_with_access(
container_permissions
),));
let container_info = run(&app, move |client, context| {
context.get_access_info(client).then(move |res| {
let mut access_info = unwrap!(res);
Ok(unwrap!(access_info.remove("_videos")).0)
})
});
let container_info = container_info.into_repr_c();
(app, container_info)
}
#[test]
fn basics() {
let (app, container_info) = setup();
let file_name0 = "file0.txt";
let ffi_file_name0 = unwrap!(CString::new(file_name0));
let res: Result<(NativeFile, u64), i32> = unsafe {
call_2(|ud, cb| dir_fetch_file(&app, &container_info, ffi_file_name0.as_ptr(), ud, cb))
};
match res {
Err(code) if code == AppError::from(NfsError::FileNotFound).error_code() => (),
Err(x) => panic!("Unexpected: {:?}", x),
Ok(_) => panic!("Unexpected success"),
}
let user_metadata = b"metadata".to_vec();
let file = NativeFile::new(user_metadata.clone());
let ffi_file = file.into_repr_c();
unsafe {
unwrap!(call_0(|ud, cb| dir_insert_file(
&app,
&container_info,
ffi_file_name0.as_ptr(),
&ffi_file,
ud,
cb,
)))
}
let (retrieved_file, retrieved_version): (NativeFile, u64) = unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name0.as_ptr(),
ud,
cb
)))
};
assert_eq!(retrieved_file.user_metadata(), &user_metadata[..]);
assert_eq!(retrieved_file.size(), 0);
assert_eq!(retrieved_version, 0);
unsafe {
unwrap!(call_0(|ud, cb| dir_delete_file(
&app,
&container_info,
ffi_file_name0.as_ptr(),
1,
ud,
cb
)))
}
}
#[test]
fn open_file() {
let (app, container_info) = setup();
let file = NativeFile::new(Vec::new());
let ffi_file = file.into_repr_c();
let file_name1 = "file1.txt";
let ffi_file_name1 = unwrap!(CString::new(file_name1));
let content = b"hello world";
let write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&ffi_file,
OPEN_MODE_OVERWRITE,
ud,
cb,
)))
};
let size: Result<u64, i32> = unsafe { call_1(|ud, cb| file_size(&app, write_h, ud, cb)) };
match size {
Err(code) if code == AppError::InvalidFileMode.error_code() => (),
Err(x) => panic!("Unexpected: {:?}", x),
Ok(_) => panic!("Unexpected success"),
}
let written_file: NativeFile = unsafe {
unwrap!(call_0(|ud, cb| file_write(
&app,
write_h,
content.as_ptr(),
content.len(),
ud,
cb
)));
unwrap!(call_1(|ud, cb| file_close(&app, write_h, ud, cb)))
};
let created_time = *written_file.created_time();
unsafe {
unwrap!(call_0(|ud, cb| dir_insert_file(
&app,
&container_info,
ffi_file_name1.as_ptr(),
&written_file.into_repr_c(),
ud,
cb,
)))
}
let (file, version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name1.as_ptr(),
ud,
cb
)))
}
};
assert_eq!(version, 0);
let size0 = file.size();
let read_write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&file.into_repr_c(),
OPEN_MODE_READ | OPEN_MODE_APPEND,
ud,
cb,
)))
};
let size1: u64 = unsafe { unwrap!(call_1(|ud, cb| file_size(&app, read_write_h, ud, cb))) };
assert_eq!(size0, size1);
let retrieved_content = unsafe {
unwrap!(call_vec_u8(|ud, cb| file_read(
&app,
read_write_h,
0,
FILE_READ_TO_END,
ud,
cb
)))
};
assert_eq!(retrieved_content, content);
let (file, _version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name1.as_ptr(),
ud,
cb
)))
}
};
let read_created_time = *file.created_time();
let read_modified_time = *file.modified_time();
assert_eq!(created_time, read_created_time);
assert!(created_time <= read_modified_time);
let append_content = b" appended";
let written_file: NativeFile = unsafe {
unwrap!(call_0(|ud, cb| file_write(
&app,
read_write_h,
append_content.as_ptr(),
append_content.len(),
ud,
cb,
)));
unwrap!(call_1(|ud, cb| file_close(&app, read_write_h, ud, cb)))
};
unsafe {
unwrap!(call_0(|ud, cb| dir_update_file(
&app,
&container_info,
ffi_file_name1.as_ptr(),
&written_file.into_repr_c(),
1,
ud,
cb,
)))
}
let (file, version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name1.as_ptr(),
ud,
cb
)))
}
};
assert_eq!(version, 1);
assert_eq!(created_time, *file.created_time());
assert!(read_modified_time <= *file.modified_time());
let orig_file = file.clone();
let read_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&file.into_repr_c(),
OPEN_MODE_READ,
ud,
cb,
)))
};
let size: u64 = unsafe { unwrap!(call_1(|ud, cb| file_size(&app, read_h, ud, cb))) };
assert_eq!(size, orig_file.size());
let retrieved_content = unsafe {
unwrap!(call_vec_u8(|ud, cb| file_read(
&app,
read_h,
0,
FILE_READ_TO_END,
ud,
cb
)))
};
assert_eq!(retrieved_content, b"hello world appended");
let returned_file: NativeFile =
unsafe { unwrap!(call_1(|ud, cb| file_close(&app, read_h, ud, cb))) };
assert_eq!(returned_file, orig_file);
}
#[test]
fn fetch_file() {
let (app, container_info) = setup();
let file = NativeFile::new(Vec::new());
let ffi_file = file.into_repr_c();
let file_name1 = "";
let ffi_file_name1 = unwrap!(CString::new(file_name1));
let content = b"hello world";
let write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&ffi_file,
OPEN_MODE_OVERWRITE,
ud,
cb,
)))
};
let written_file: NativeFile = unsafe {
unwrap!(call_0(|ud, cb| file_write(
&app,
write_h,
content.as_ptr(),
content.len(),
ud,
cb
)));
unwrap!(call_1(|ud, cb| file_close(&app, write_h, ud, cb)))
};
unsafe {
unwrap!(call_0(|ud, cb| dir_insert_file(
&app,
&container_info,
ffi_file_name1.as_ptr(),
&written_file.into_repr_c(),
ud,
cb,
)))
}
let (file, version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name1.as_ptr(),
ud,
cb
)))
}
};
assert_eq!(version, 0);
let size0 = file.size();
let read_write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&file.into_repr_c(),
OPEN_MODE_READ | OPEN_MODE_APPEND,
ud,
cb,
)))
};
let size1: u64 = unsafe { unwrap!(call_1(|ud, cb| file_size(&app, read_write_h, ud, cb))) };
assert_eq!(size0, size1);
let retrieved_content = unsafe {
unwrap!(call_vec_u8(|ud, cb| file_read(
&app,
read_write_h,
0,
FILE_READ_TO_END,
ud,
cb
)))
};
assert_eq!(retrieved_content, content);
std::thread::sleep(std::time::Duration::new(2, 0));
let (file, version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name1.as_ptr(),
ud,
cb
)))
}
};
assert_eq!(version, 0);
let size0 = file.size();
let read_write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&file.into_repr_c(),
OPEN_MODE_READ | OPEN_MODE_APPEND,
ud,
cb,
)))
};
let size1: u64 = unsafe { unwrap!(call_1(|ud, cb| file_size(&app, read_write_h, ud, cb))) };
assert_eq!(size0, size1);
let retrieved_content = unsafe {
unwrap!(call_vec_u8(|ud, cb| file_read(
&app,
read_write_h,
0,
FILE_READ_TO_END,
ud,
cb
)))
};
assert_eq!(retrieved_content, content);
}
#[test]
fn delete_then_open_file() {
let (app, container_info) = setup();
let file = NativeFile::new(Vec::new());
let ffi_file = file.into_repr_c();
let file_name2 = "file2.txt";
let ffi_file_name2 = unwrap!(CString::new(file_name2));
let content_original = b"hello world";
let write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&ffi_file,
OPEN_MODE_OVERWRITE,
ud,
cb,
)))
};
let written_file: NativeFile = unsafe {
unwrap!(call_0(|ud, cb| file_write(
&app,
write_h,
content_original.as_ptr(),
content_original.len(),
ud,
cb,
)));
unwrap!(call_1(|ud, cb| file_close(&app, write_h, ud, cb)))
};
unsafe {
unwrap!(call_0(|ud, cb| dir_insert_file(
&app,
&container_info,
ffi_file_name2.as_ptr(),
&written_file.into_repr_c(),
ud,
cb,
)))
}
unsafe {
unwrap!(call_0(|ud, cb| dir_delete_file(
&app,
&container_info,
ffi_file_name2.as_ptr(),
1,
ud,
cb
)))
}
let file = NativeFile::new(Vec::new());
let ffi_file = file.into_repr_c();
let content_new = b"hello goodbye";
let write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&ffi_file,
OPEN_MODE_OVERWRITE,
ud,
cb,
)))
};
let new_file: NativeFile = unsafe {
unwrap!(call_0(|ud, cb| file_write(
&app,
write_h,
content_new.as_ptr(),
content_new.len(),
ud,
cb,
)));
unwrap!(call_1(|ud, cb| file_close(&app, write_h, ud, cb)))
};
unsafe {
unwrap!(call_0(|ud, cb| dir_update_file(
&app,
&container_info,
ffi_file_name2.as_ptr(),
&new_file.into_repr_c(),
2,
ud,
cb,
)))
}
let (file, version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name2.as_ptr(),
ud,
cb
)))
}
};
assert_eq!(version, 2);
let read_write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&file.into_repr_c(),
OPEN_MODE_READ | OPEN_MODE_APPEND,
ud,
cb,
)))
};
let retrieved_content = unsafe {
unwrap!(call_vec_u8(|ud, cb| file_read(
&app,
read_write_h,
0,
FILE_READ_TO_END,
ud,
cb
)))
};
assert_eq!(retrieved_content, content_new);
}
#[test]
fn open_close_file() {
let (app, container_info) = setup();
let file_name = "file0.txt";
let ffi_file_name = unwrap!(CString::new(file_name));
let user_metadata = b"metadata".to_vec();
let file = NativeFile::new(user_metadata.clone());
let ffi_file = file.into_repr_c();
let content = b"hello world";
let write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&ffi_file,
OPEN_MODE_OVERWRITE,
ud,
cb,
)))
};
let written_file: NativeFile = unsafe {
unwrap!(call_0(|ud, cb| file_write(
&app,
write_h,
content.as_ptr(),
content.len(),
ud,
cb
)));
unwrap!(call_1(|ud, cb| file_close(&app, write_h, ud, cb)))
};
unsafe {
unwrap!(call_0(|ud, cb| dir_insert_file(
&app,
&container_info,
ffi_file_name.as_ptr(),
&written_file.into_repr_c(),
ud,
cb,
)))
}
let (file, _version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name.as_ptr(),
ud,
cb
)))
}
};
let read_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&file.into_repr_c(),
OPEN_MODE_READ,
ud,
cb,
)))
};
let _: *const File = unsafe { unwrap!(call_1(|ud, cb| file_close(&app, read_h, ud, cb))) };
let (file, _version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name.as_ptr(),
ud,
cb
)))
}
};
let write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&file.into_repr_c(),
OPEN_MODE_OVERWRITE,
ud,
cb,
)))
};
let _: NativeFile = unsafe { unwrap!(call_1(|ud, cb| file_close(&app, write_h, ud, cb))) };
let (file, _version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name.as_ptr(),
ud,
cb
)))
}
};
let append_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&file.into_repr_c(),
OPEN_MODE_APPEND,
ud,
cb,
)))
};
let _: NativeFile = unsafe { unwrap!(call_1(|ud, cb| file_close(&app, append_h, ud, cb))) };
}
#[test]
fn file_read_chunks() {
const ORIG_SIZE: usize = 5555;
let (app, container_info) = setup();
let file = NativeFile::new(Vec::new());
let ffi_file = file.into_repr_c();
let file_name = "file.txt";
let ffi_file_name = unwrap!(CString::new(file_name));
let content = [0u8; ORIG_SIZE];
let write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&ffi_file,
OPEN_MODE_OVERWRITE,
ud,
cb,
)))
};
let written_file: NativeFile = unsafe {
unwrap!(call_0(|ud, cb| file_write(
&app,
write_h,
content.as_ptr(),
content.len(),
ud,
cb
)));
unwrap!(call_1(|ud, cb| file_close(&app, write_h, ud, cb)))
};
unsafe {
unwrap!(call_0(|ud, cb| dir_insert_file(
&app,
&container_info,
ffi_file_name.as_ptr(),
&written_file.into_repr_c(),
ud,
cb,
)))
}
let (file, version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name.as_ptr(),
ud,
cb
)))
}
};
assert_eq!(version, 0);
let read_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&file.into_repr_c(),
OPEN_MODE_READ,
ud,
cb,
)))
};
let size = unsafe { unwrap!(call_1(|ud, cb| file_size(&app, read_h, ud, cb))) };
const CHUNK_SIZE: u64 = 1000;
let mut size_read = 0;
let mut result = Vec::new();
while size_read < size {
let to_read = if size_read + CHUNK_SIZE >= size {
size - size_read
} else {
CHUNK_SIZE
};
let mut retrieved_content = unsafe {
unwrap!(call_vec_u8(|ud, cb| file_read(
&app, read_h, size_read, to_read, ud, cb
),))
};
size_read += retrieved_content.len() as u64;
result.append(&mut retrieved_content);
}
assert_eq!(size, size_read);
assert_eq!(result, vec![0u8; ORIG_SIZE]);
let retrieved_content = unsafe {
unwrap!(call_vec_u8(|ud, cb| file_read(
&app, read_h, size, 0, ud, cb
),))
};
assert_eq!(retrieved_content, Vec::<u8>::new());
let retrieved_content =
unsafe { call_vec_u8(|ud, cb| file_read(&app, read_h, size, 1, ud, cb)) };
match retrieved_content {
Err(code) if code == AppError::from(NfsError::InvalidRange).error_code() => (),
Err(x) => panic!("Unexpected: {:?}", x),
Ok(_) => panic!("Unexpected success"),
}
}
#[test]
fn file_write_chunks() {
const GOAL_SIZE: usize = 5555;
const CHUNK_SIZE: usize = 1000;
let (app, container_info) = setup();
let content = [0u8; GOAL_SIZE];
let file = NativeFile::new(Vec::new());
let ffi_file = file.into_repr_c();
let file_name = "file.txt";
let ffi_file_name = unwrap!(CString::new(file_name));
let write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&ffi_file,
OPEN_MODE_OVERWRITE,
ud,
cb,
)))
};
let written_file = write_chunks(&app, write_h, &content, GOAL_SIZE, CHUNK_SIZE);
unsafe {
unwrap!(call_0(|ud, cb| dir_insert_file(
&app,
&container_info,
ffi_file_name.as_ptr(),
&written_file.into_repr_c(),
ud,
cb,
)))
}
let (file, version): (NativeFile, u64) = {
unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name.as_ptr(),
ud,
cb
)))
}
};
assert_eq!(version, 0);
let read_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&file.into_repr_c(),
OPEN_MODE_READ,
ud,
cb,
)))
};
let retrieved_content = unsafe {
unwrap!(call_vec_u8(|ud, cb| file_read(
&app,
read_h,
0,
FILE_READ_TO_END,
ud,
cb
)))
};
assert_eq!(retrieved_content, vec![0u8; GOAL_SIZE]);
let (file, version): (NativeFile, u64) = unsafe {
unwrap!(call_2(|ud, cb| dir_fetch_file(
&app,
&container_info,
ffi_file_name.as_ptr(),
ud,
cb
)))
};
assert_eq!(version, 0);
let ffi_file = file.into_repr_c();
let write_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&ffi_file,
OPEN_MODE_APPEND,
ud,
cb
)))
};
let written_file = write_chunks(&app, write_h, &content, GOAL_SIZE, CHUNK_SIZE);
let read_h = unsafe {
unwrap!(call_1(|ud, cb| file_open(
&app,
&container_info,
&written_file.into_repr_c(),
OPEN_MODE_READ,
ud,
cb,
)))
};
let retrieved_content = unsafe {
unwrap!(call_vec_u8(|ud, cb| file_read(
&app,
read_h,
0,
FILE_READ_TO_END,
ud,
cb
)))
};
assert_eq!(retrieved_content, vec![0u8; 2 * GOAL_SIZE]);
}
fn write_chunks(
app: &App,
write_h: FileContextHandle,
content: &[u8],
goal_size: usize,
chunk_size: usize,
) -> NativeFile {
let mut size_written = 0;
while size_written < goal_size {
let to_write = if size_written + chunk_size > goal_size {
goal_size - size_written
} else {
chunk_size
};
println!("Writing {} bytes to file", to_write);
unsafe {
unwrap!(call_0(|ud, cb| file_write(
app,
write_h,
content.get_unchecked(size_written),
to_write,
ud,
cb,
)))
}
size_written += to_write;
}
unsafe {
println!("Writing 0 bytes to file");
unwrap!(call_0(|ud, cb| file_write(
app,
write_h,
content.get_unchecked(goal_size),
0,
ud,
cb
)));
unwrap!(call_1(|ud, cb| file_close(app, write_h, ud, cb)))
}
}