Skip to content

Commit

Permalink
Tests (#14)
Browse files Browse the repository at this point in the history
* Add writer tests

* Zero out memory

* Update SHA256

* Update Rust version

* Add writer seek tests

* Renaming

* Add reader tests

* Test vectored write, seek to start on teardown

* Add test setup

* Add vectored read test

* Add test for read_to_end, change read EOF behavior

* Test read_exact

* Test seeking past end

* Test seeking before zero
  • Loading branch information
paulyoung authored Apr 7, 2022
1 parent 794b642 commit e9ddc32
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 6 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
members = [
"crates/icfs",
"crates/icfs-fatfs",
"examples/icfs",
"examples/fatfs",
]
6 changes: 3 additions & 3 deletions crates/icfs/stable_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use ic_cdk::api::stable::{
};
use std::io;

#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct StableMemory {
offset: usize,
}
Expand Down Expand Up @@ -140,8 +140,8 @@ impl Default for StableMemory {

impl std::io::Read for StableMemory {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
// self.read(buf).or(Ok(0)) // Read defines EOF to be success
read(self, buf).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
// read(self, buf).map_err(|e| io::Error::new(io::ErrorKind::Other, e))
read(self, buf).or(Ok(0)) // Read defines EOF to be success
}
}

Expand Down
6 changes: 6 additions & 0 deletions dfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
"version": 1,
"dfx": "0.8.4",
"canisters": {
"icfs": {
"type": "custom",
"build": "nix build '.#icfs-example'",
"candid": "examples/icfs/icfs.did",
"wasm": "result/lib/icfs_example.wasm"
},
"fatfs": {
"type": "custom",
"build": "nix build '.#fatfs-example'",
Expand Down
14 changes: 14 additions & 0 deletions examples/icfs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "icfs-example"
version = "0.1.0"
edition = "2018"
authors = ["Paul Young <[email protected]>"]

[lib]
path = "lib.rs"
crate-type = ["cdylib", "lib"]

[dependencies]
ic-cdk = { git = "https://github.com/dfinity/cdk-rs.git", rev = "a253119adb08929b6304d007ee0a6a37960656ed" }
ic-cdk-macros = "0.3"
icfs = { path = "../../crates/icfs" }
11 changes: 11 additions & 0 deletions examples/icfs/icfs.did
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
service : {
test_writer : () -> ();
test_writer_vectored : () -> ();
test_writer_seek : () -> ();
test_reader : () -> ();
test_reader_vectored : () -> ();
test_read_to_end : () -> ();
test_read_exact : () -> ();
test_seek_past_end : () -> ();
test_seek_before_0 : () -> ();
}
262 changes: 262 additions & 0 deletions examples/icfs/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
// Based on:
// * https://users.rust-lang.org/t/existing-tests-for-read-write-and-seek-traits/72991/2
// * https://github.com/rust-lang/rust/blob/a2ebd5a1f12f4242edf66cbbd471c421bec62753/library/std/src/io/cursor/tests.rs

#![feature(io_slice_advance)]
#![feature(write_all_vectored)]

use ic_cdk_macros::{init, query, update};
use std::io::{IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};

thread_local! {
static STABLE_MEMORY: std::cell::RefCell<icfs::StableMemory>
= std::cell::RefCell::new(icfs::StableMemory::default());
}

fn setup() {
STABLE_MEMORY.with(|stable_memory| {
let mut stable_memory = *stable_memory.borrow();
let capacity = icfs::StableMemory::capacity();
let b: &[_] = &vec![0; capacity];

ic_cdk::api::stable::stable64_write(0, &b);
assert_eq!(&icfs::StableMemory::bytes()[..], b);

stable_memory.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(stable_memory.stream_position().unwrap(), 0);
})
}

#[update]
fn test_writer() {
setup();
STABLE_MEMORY.with(|stable_memory| {
let mut stable_memory = *stable_memory.borrow();
assert_eq!(stable_memory.write(&[0]).unwrap(), 1);
assert_eq!(stable_memory.write(&[1, 2, 3]).unwrap(), 3);
assert_eq!(stable_memory.write(&[4, 5, 6, 7]).unwrap(), 4);
stable_memory
.write_all_vectored(&mut [
IoSlice::new(&[]),
IoSlice::new(&[8, 9]),
IoSlice::new(&[10]),
])
.unwrap();
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assert_eq!(&icfs::StableMemory::bytes()[0..11], b);
})
}

#[update]
fn test_writer_vectored() {
setup();
STABLE_MEMORY.with(|stable_memory| {
let mut stable_memory = *stable_memory.borrow();
assert_eq!(stable_memory.stream_position().unwrap(), 0);

stable_memory.write_all_vectored(&mut [IoSlice::new(&[0])]).unwrap();
assert_eq!(stable_memory.stream_position().unwrap(), 1);

stable_memory.write_all_vectored(&mut [IoSlice::new(&mut [1, 2, 3]), IoSlice::new(&mut [4, 5, 6, 7]),]).unwrap();
assert_eq!(stable_memory.stream_position().unwrap(), 8);

stable_memory.write_all_vectored(&mut []).unwrap();
assert_eq!(stable_memory.stream_position().unwrap(), 8);

stable_memory.write_all_vectored(&mut [IoSlice::new(&[8, 9])]).unwrap();
stable_memory.write_all_vectored(&mut [IoSlice::new(&[10])]).unwrap();

let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
assert_eq!(&icfs::StableMemory::bytes()[0..9], b);
})
}

#[update]
fn test_writer_seek() {
setup();
STABLE_MEMORY.with(|stable_memory| {
let mut stable_memory = *stable_memory.borrow();

assert_eq!(stable_memory.stream_position().unwrap(), 0);
assert_eq!(stable_memory.write(&[1]).unwrap(), 1);
assert_eq!(stable_memory.stream_position().unwrap(), 1);

assert_eq!(stable_memory.seek(SeekFrom::Start(2)).unwrap(), 2);
assert_eq!(stable_memory.stream_position().unwrap(), 2);
assert_eq!(stable_memory.write(&[2]).unwrap(), 1);
assert_eq!(stable_memory.stream_position().unwrap(), 3);

assert_eq!(stable_memory.seek(SeekFrom::Current(-2)).unwrap(), 1);
assert_eq!(stable_memory.stream_position().unwrap(), 1);
assert_eq!(stable_memory.write(&[3]).unwrap(), 1);
assert_eq!(stable_memory.stream_position().unwrap(), 2);

let capacity = icfs::StableMemory::capacity();

assert_eq!(stable_memory.seek(SeekFrom::End(-1)).unwrap(), capacity as u64 - 1);
assert_eq!(stable_memory.stream_position().unwrap(), capacity as u64 - 1);
assert_eq!(stable_memory.write(&[4]).unwrap(), 1);
assert_eq!(stable_memory.stream_position().unwrap(), capacity as u64);

let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 0];
assert_eq!(&icfs::StableMemory::bytes()[0..8], b);

let b: &[_] = &[0, 0, 0, 0, 0, 0, 0, 4];
assert_eq!(&icfs::StableMemory::bytes()[(capacity - 8)..], b);
})
}

#[update]
fn test_reader() {
setup();
STABLE_MEMORY.with(|stable_memory| {
let mut stable_memory = *stable_memory.borrow();
stable_memory.write(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
stable_memory.seek(SeekFrom::Start(0)).unwrap();

let mut buf = [];
assert_eq!(stable_memory.read(&mut buf).unwrap(), 0);
assert_eq!(stable_memory.stream_position().unwrap(), 0);

let mut buf = [0];
assert_eq!(stable_memory.read(&mut buf).unwrap(), 1);
assert_eq!(stable_memory.stream_position().unwrap(), 1);

let b: &[_] = &[0];
assert_eq!(buf, b);

let mut buf = [0; 4];
assert_eq!(stable_memory.read(&mut buf).unwrap(), 4);
assert_eq!(stable_memory.stream_position().unwrap(), 5);

let b: &[_] = &[1, 2, 3, 4];
assert_eq!(buf, b);
assert_eq!(stable_memory.read(&mut buf).unwrap(), 4);

let b: &[_] = &[5, 6, 7, 0];
assert_eq!(buf, b);

let b: &[_] = &[5, 6, 7];
assert_eq!(&buf[..3], b);

assert_eq!(stable_memory.read(&mut buf).unwrap(), 4);
let b: &[_] = &[0, 0, 0, 0];
assert_eq!(buf, b);
})
}

// Based on https://github.com/rust-lang/rust/blob/a2af9cf1cf6ccb195eae40cdd793939bc77e7e73/library/std/src/io/mod.rs#L1578
fn read_all_vectored(stable_memory: &mut icfs::StableMemory, mut bufs: &mut [IoSliceMut<'_>]) -> std::io::Result<()> {
// Guarantee that bufs is empty if it contains no data,
// to avoid calling write_vectored if there is no data to be written.
IoSliceMut::advance_slices(&mut bufs, 0);
while !bufs.is_empty() {
match stable_memory.read_vectored(bufs) {
Ok(0) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"failed to read whole buffer",
));
}
Ok(n) => IoSliceMut::advance_slices(&mut bufs, n),
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}

#[update]
fn test_reader_vectored() {
setup();
STABLE_MEMORY.with(|stable_memory| {
let mut stable_memory = *stable_memory.borrow();
stable_memory.write(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
stable_memory.seek(SeekFrom::Start(0)).unwrap();

let mut buf = [];
read_all_vectored(&mut stable_memory, &mut [IoSliceMut::new(&mut buf)]).unwrap();
assert_eq!(stable_memory.stream_position().unwrap(), 0);

let mut buf = [0];
read_all_vectored(&mut stable_memory, &mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap();
assert_eq!(stable_memory.stream_position().unwrap(), 1);

let b: &[_] = &[0];
assert_eq!(buf, b);

let mut buf1 = [0; 4];
let mut buf2 = [0; 4];
read_all_vectored(&mut stable_memory, &mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),]).unwrap();

let b1: &[_] = &[1, 2, 3, 4];
let b2: &[_] = &[5, 6, 7];
assert_eq!(buf1, b1);
assert_eq!(&buf2[..3], b2);

assert_eq!(stable_memory.read(&mut buf).unwrap(), 1);
})
}

#[update]
fn test_read_to_end() {
setup();
STABLE_MEMORY.with(|stable_memory| {
let mut stable_memory = *stable_memory.borrow();
stable_memory.write(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
stable_memory.seek(SeekFrom::Start(0)).unwrap();

let mut v = Vec::new();
stable_memory.read_to_end(&mut v).unwrap();

assert_eq!(v, icfs::StableMemory::bytes());
})
}

#[update]
fn test_read_exact() {
setup();
STABLE_MEMORY.with(|stable_memory| {
let mut stable_memory = *stable_memory.borrow();
stable_memory.write(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
stable_memory.seek(SeekFrom::Start(0)).unwrap();

let mut buf = [];
assert!(stable_memory.read_exact(&mut buf).is_ok());

let mut buf = [8];
assert!(stable_memory.read_exact(&mut buf).is_ok());
assert_eq!(buf[0], 0);

let mut buf = [0, 0, 0, 0, 0, 0, 0];
assert!(stable_memory.read_exact(&mut buf).is_ok());
assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]);
})
}

#[update]
fn test_seek_past_end() {
setup();
STABLE_MEMORY.with(|stable_memory| {
let mut stable_memory = *stable_memory.borrow();
let capacity = icfs::StableMemory::capacity();
let offset = capacity as u64 + 1;
assert_eq!(stable_memory.seek(SeekFrom::Start(offset)).unwrap(), offset);
assert_eq!(stable_memory.read(&mut [0]).unwrap(), 0);
})
}

#[update]
fn test_seek_before_0() {
setup();
STABLE_MEMORY.with(|stable_memory| {
let mut stable_memory = *stable_memory.borrow();
stable_memory.seek(SeekFrom::Start(0)).unwrap();
assert!(stable_memory.seek(SeekFrom::Current(-1)).is_err());

stable_memory.seek(SeekFrom::Start(0)).unwrap();
let capacity = icfs::StableMemory::capacity();
let offset = 0 - capacity as i64 - 1;
assert!(stable_memory.seek(SeekFrom::End(offset)).is_err());
})
}
32 changes: 32 additions & 0 deletions examples/icfs/test.ic-repl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Based on:
// * https://users.rust-lang.org/t/existing-tests-for-read-write-and-seek-traits/72991/2
// * https://github.com/rust-lang/rust/blob/a2ebd5a1f12f4242edf66cbbd471c421bec62753/library/std/src/io/cursor/tests.rs

import icfs = "rrkah-fqaaa-aaaaa-aaaaq-cai" as "icfs.did";

let result = call icfs.test_writer();
assert result == null;

let result = call icfs.test_writer_vectored();
assert result == null;

let result = call icfs.test_writer_seek();
assert result == null;

let result = call icfs.test_reader();
assert result == null;

let result = call icfs.test_reader_vectored();
assert result == null;

let result = call icfs.test_read_to_end();
assert result == null;

let result = call icfs.test_read_exact();
assert result == null;

let result = call icfs.test_seek_past_end();
assert result == null;

let result = call icfs.test_seek_before_0();
assert result == null;
Loading

0 comments on commit e9ddc32

Please sign in to comment.