Skip to content

Commit e9ddc32

Browse files
authored
Tests (#14)
* 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
1 parent 794b642 commit e9ddc32

File tree

9 files changed

+349
-6
lines changed

9 files changed

+349
-6
lines changed

Diff for: Cargo.lock

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
members = [
33
"crates/icfs",
44
"crates/icfs-fatfs",
5+
"examples/icfs",
56
"examples/fatfs",
67
]

Diff for: crates/icfs/stable_memory.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ic_cdk::api::stable::{
55
};
66
use std::io;
77

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

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

Diff for: dfx.json

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
"version": 1,
33
"dfx": "0.8.4",
44
"canisters": {
5+
"icfs": {
6+
"type": "custom",
7+
"build": "nix build '.#icfs-example'",
8+
"candid": "examples/icfs/icfs.did",
9+
"wasm": "result/lib/icfs_example.wasm"
10+
},
511
"fatfs": {
612
"type": "custom",
713
"build": "nix build '.#fatfs-example'",

Diff for: examples/icfs/Cargo.toml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "icfs-example"
3+
version = "0.1.0"
4+
edition = "2018"
5+
authors = ["Paul Young <[email protected]>"]
6+
7+
[lib]
8+
path = "lib.rs"
9+
crate-type = ["cdylib", "lib"]
10+
11+
[dependencies]
12+
ic-cdk = { git = "https://github.com/dfinity/cdk-rs.git", rev = "a253119adb08929b6304d007ee0a6a37960656ed" }
13+
ic-cdk-macros = "0.3"
14+
icfs = { path = "../../crates/icfs" }

Diff for: examples/icfs/icfs.did

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
service : {
2+
test_writer : () -> ();
3+
test_writer_vectored : () -> ();
4+
test_writer_seek : () -> ();
5+
test_reader : () -> ();
6+
test_reader_vectored : () -> ();
7+
test_read_to_end : () -> ();
8+
test_read_exact : () -> ();
9+
test_seek_past_end : () -> ();
10+
test_seek_before_0 : () -> ();
11+
}

Diff for: examples/icfs/lib.rs

+262
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
// Based on:
2+
// * https://users.rust-lang.org/t/existing-tests-for-read-write-and-seek-traits/72991/2
3+
// * https://github.com/rust-lang/rust/blob/a2ebd5a1f12f4242edf66cbbd471c421bec62753/library/std/src/io/cursor/tests.rs
4+
5+
#![feature(io_slice_advance)]
6+
#![feature(write_all_vectored)]
7+
8+
use ic_cdk_macros::{init, query, update};
9+
use std::io::{IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
10+
11+
thread_local! {
12+
static STABLE_MEMORY: std::cell::RefCell<icfs::StableMemory>
13+
= std::cell::RefCell::new(icfs::StableMemory::default());
14+
}
15+
16+
fn setup() {
17+
STABLE_MEMORY.with(|stable_memory| {
18+
let mut stable_memory = *stable_memory.borrow();
19+
let capacity = icfs::StableMemory::capacity();
20+
let b: &[_] = &vec![0; capacity];
21+
22+
ic_cdk::api::stable::stable64_write(0, &b);
23+
assert_eq!(&icfs::StableMemory::bytes()[..], b);
24+
25+
stable_memory.seek(SeekFrom::Start(0)).unwrap();
26+
assert_eq!(stable_memory.stream_position().unwrap(), 0);
27+
})
28+
}
29+
30+
#[update]
31+
fn test_writer() {
32+
setup();
33+
STABLE_MEMORY.with(|stable_memory| {
34+
let mut stable_memory = *stable_memory.borrow();
35+
assert_eq!(stable_memory.write(&[0]).unwrap(), 1);
36+
assert_eq!(stable_memory.write(&[1, 2, 3]).unwrap(), 3);
37+
assert_eq!(stable_memory.write(&[4, 5, 6, 7]).unwrap(), 4);
38+
stable_memory
39+
.write_all_vectored(&mut [
40+
IoSlice::new(&[]),
41+
IoSlice::new(&[8, 9]),
42+
IoSlice::new(&[10]),
43+
])
44+
.unwrap();
45+
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
46+
assert_eq!(&icfs::StableMemory::bytes()[0..11], b);
47+
})
48+
}
49+
50+
#[update]
51+
fn test_writer_vectored() {
52+
setup();
53+
STABLE_MEMORY.with(|stable_memory| {
54+
let mut stable_memory = *stable_memory.borrow();
55+
assert_eq!(stable_memory.stream_position().unwrap(), 0);
56+
57+
stable_memory.write_all_vectored(&mut [IoSlice::new(&[0])]).unwrap();
58+
assert_eq!(stable_memory.stream_position().unwrap(), 1);
59+
60+
stable_memory.write_all_vectored(&mut [IoSlice::new(&mut [1, 2, 3]), IoSlice::new(&mut [4, 5, 6, 7]),]).unwrap();
61+
assert_eq!(stable_memory.stream_position().unwrap(), 8);
62+
63+
stable_memory.write_all_vectored(&mut []).unwrap();
64+
assert_eq!(stable_memory.stream_position().unwrap(), 8);
65+
66+
stable_memory.write_all_vectored(&mut [IoSlice::new(&[8, 9])]).unwrap();
67+
stable_memory.write_all_vectored(&mut [IoSlice::new(&[10])]).unwrap();
68+
69+
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
70+
assert_eq!(&icfs::StableMemory::bytes()[0..9], b);
71+
})
72+
}
73+
74+
#[update]
75+
fn test_writer_seek() {
76+
setup();
77+
STABLE_MEMORY.with(|stable_memory| {
78+
let mut stable_memory = *stable_memory.borrow();
79+
80+
assert_eq!(stable_memory.stream_position().unwrap(), 0);
81+
assert_eq!(stable_memory.write(&[1]).unwrap(), 1);
82+
assert_eq!(stable_memory.stream_position().unwrap(), 1);
83+
84+
assert_eq!(stable_memory.seek(SeekFrom::Start(2)).unwrap(), 2);
85+
assert_eq!(stable_memory.stream_position().unwrap(), 2);
86+
assert_eq!(stable_memory.write(&[2]).unwrap(), 1);
87+
assert_eq!(stable_memory.stream_position().unwrap(), 3);
88+
89+
assert_eq!(stable_memory.seek(SeekFrom::Current(-2)).unwrap(), 1);
90+
assert_eq!(stable_memory.stream_position().unwrap(), 1);
91+
assert_eq!(stable_memory.write(&[3]).unwrap(), 1);
92+
assert_eq!(stable_memory.stream_position().unwrap(), 2);
93+
94+
let capacity = icfs::StableMemory::capacity();
95+
96+
assert_eq!(stable_memory.seek(SeekFrom::End(-1)).unwrap(), capacity as u64 - 1);
97+
assert_eq!(stable_memory.stream_position().unwrap(), capacity as u64 - 1);
98+
assert_eq!(stable_memory.write(&[4]).unwrap(), 1);
99+
assert_eq!(stable_memory.stream_position().unwrap(), capacity as u64);
100+
101+
let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 0];
102+
assert_eq!(&icfs::StableMemory::bytes()[0..8], b);
103+
104+
let b: &[_] = &[0, 0, 0, 0, 0, 0, 0, 4];
105+
assert_eq!(&icfs::StableMemory::bytes()[(capacity - 8)..], b);
106+
})
107+
}
108+
109+
#[update]
110+
fn test_reader() {
111+
setup();
112+
STABLE_MEMORY.with(|stable_memory| {
113+
let mut stable_memory = *stable_memory.borrow();
114+
stable_memory.write(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
115+
stable_memory.seek(SeekFrom::Start(0)).unwrap();
116+
117+
let mut buf = [];
118+
assert_eq!(stable_memory.read(&mut buf).unwrap(), 0);
119+
assert_eq!(stable_memory.stream_position().unwrap(), 0);
120+
121+
let mut buf = [0];
122+
assert_eq!(stable_memory.read(&mut buf).unwrap(), 1);
123+
assert_eq!(stable_memory.stream_position().unwrap(), 1);
124+
125+
let b: &[_] = &[0];
126+
assert_eq!(buf, b);
127+
128+
let mut buf = [0; 4];
129+
assert_eq!(stable_memory.read(&mut buf).unwrap(), 4);
130+
assert_eq!(stable_memory.stream_position().unwrap(), 5);
131+
132+
let b: &[_] = &[1, 2, 3, 4];
133+
assert_eq!(buf, b);
134+
assert_eq!(stable_memory.read(&mut buf).unwrap(), 4);
135+
136+
let b: &[_] = &[5, 6, 7, 0];
137+
assert_eq!(buf, b);
138+
139+
let b: &[_] = &[5, 6, 7];
140+
assert_eq!(&buf[..3], b);
141+
142+
assert_eq!(stable_memory.read(&mut buf).unwrap(), 4);
143+
let b: &[_] = &[0, 0, 0, 0];
144+
assert_eq!(buf, b);
145+
})
146+
}
147+
148+
// Based on https://github.com/rust-lang/rust/blob/a2af9cf1cf6ccb195eae40cdd793939bc77e7e73/library/std/src/io/mod.rs#L1578
149+
fn read_all_vectored(stable_memory: &mut icfs::StableMemory, mut bufs: &mut [IoSliceMut<'_>]) -> std::io::Result<()> {
150+
// Guarantee that bufs is empty if it contains no data,
151+
// to avoid calling write_vectored if there is no data to be written.
152+
IoSliceMut::advance_slices(&mut bufs, 0);
153+
while !bufs.is_empty() {
154+
match stable_memory.read_vectored(bufs) {
155+
Ok(0) => {
156+
return Err(std::io::Error::new(
157+
std::io::ErrorKind::Other,
158+
"failed to read whole buffer",
159+
));
160+
}
161+
Ok(n) => IoSliceMut::advance_slices(&mut bufs, n),
162+
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
163+
Err(e) => return Err(e),
164+
}
165+
}
166+
Ok(())
167+
}
168+
169+
#[update]
170+
fn test_reader_vectored() {
171+
setup();
172+
STABLE_MEMORY.with(|stable_memory| {
173+
let mut stable_memory = *stable_memory.borrow();
174+
stable_memory.write(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
175+
stable_memory.seek(SeekFrom::Start(0)).unwrap();
176+
177+
let mut buf = [];
178+
read_all_vectored(&mut stable_memory, &mut [IoSliceMut::new(&mut buf)]).unwrap();
179+
assert_eq!(stable_memory.stream_position().unwrap(), 0);
180+
181+
let mut buf = [0];
182+
read_all_vectored(&mut stable_memory, &mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap();
183+
assert_eq!(stable_memory.stream_position().unwrap(), 1);
184+
185+
let b: &[_] = &[0];
186+
assert_eq!(buf, b);
187+
188+
let mut buf1 = [0; 4];
189+
let mut buf2 = [0; 4];
190+
read_all_vectored(&mut stable_memory, &mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),]).unwrap();
191+
192+
let b1: &[_] = &[1, 2, 3, 4];
193+
let b2: &[_] = &[5, 6, 7];
194+
assert_eq!(buf1, b1);
195+
assert_eq!(&buf2[..3], b2);
196+
197+
assert_eq!(stable_memory.read(&mut buf).unwrap(), 1);
198+
})
199+
}
200+
201+
#[update]
202+
fn test_read_to_end() {
203+
setup();
204+
STABLE_MEMORY.with(|stable_memory| {
205+
let mut stable_memory = *stable_memory.borrow();
206+
stable_memory.write(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
207+
stable_memory.seek(SeekFrom::Start(0)).unwrap();
208+
209+
let mut v = Vec::new();
210+
stable_memory.read_to_end(&mut v).unwrap();
211+
212+
assert_eq!(v, icfs::StableMemory::bytes());
213+
})
214+
}
215+
216+
#[update]
217+
fn test_read_exact() {
218+
setup();
219+
STABLE_MEMORY.with(|stable_memory| {
220+
let mut stable_memory = *stable_memory.borrow();
221+
stable_memory.write(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
222+
stable_memory.seek(SeekFrom::Start(0)).unwrap();
223+
224+
let mut buf = [];
225+
assert!(stable_memory.read_exact(&mut buf).is_ok());
226+
227+
let mut buf = [8];
228+
assert!(stable_memory.read_exact(&mut buf).is_ok());
229+
assert_eq!(buf[0], 0);
230+
231+
let mut buf = [0, 0, 0, 0, 0, 0, 0];
232+
assert!(stable_memory.read_exact(&mut buf).is_ok());
233+
assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]);
234+
})
235+
}
236+
237+
#[update]
238+
fn test_seek_past_end() {
239+
setup();
240+
STABLE_MEMORY.with(|stable_memory| {
241+
let mut stable_memory = *stable_memory.borrow();
242+
let capacity = icfs::StableMemory::capacity();
243+
let offset = capacity as u64 + 1;
244+
assert_eq!(stable_memory.seek(SeekFrom::Start(offset)).unwrap(), offset);
245+
assert_eq!(stable_memory.read(&mut [0]).unwrap(), 0);
246+
})
247+
}
248+
249+
#[update]
250+
fn test_seek_before_0() {
251+
setup();
252+
STABLE_MEMORY.with(|stable_memory| {
253+
let mut stable_memory = *stable_memory.borrow();
254+
stable_memory.seek(SeekFrom::Start(0)).unwrap();
255+
assert!(stable_memory.seek(SeekFrom::Current(-1)).is_err());
256+
257+
stable_memory.seek(SeekFrom::Start(0)).unwrap();
258+
let capacity = icfs::StableMemory::capacity();
259+
let offset = 0 - capacity as i64 - 1;
260+
assert!(stable_memory.seek(SeekFrom::End(offset)).is_err());
261+
})
262+
}

Diff for: examples/icfs/test.ic-repl

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Based on:
2+
// * https://users.rust-lang.org/t/existing-tests-for-read-write-and-seek-traits/72991/2
3+
// * https://github.com/rust-lang/rust/blob/a2ebd5a1f12f4242edf66cbbd471c421bec62753/library/std/src/io/cursor/tests.rs
4+
5+
import icfs = "rrkah-fqaaa-aaaaa-aaaaq-cai" as "icfs.did";
6+
7+
let result = call icfs.test_writer();
8+
assert result == null;
9+
10+
let result = call icfs.test_writer_vectored();
11+
assert result == null;
12+
13+
let result = call icfs.test_writer_seek();
14+
assert result == null;
15+
16+
let result = call icfs.test_reader();
17+
assert result == null;
18+
19+
let result = call icfs.test_reader_vectored();
20+
assert result == null;
21+
22+
let result = call icfs.test_read_to_end();
23+
assert result == null;
24+
25+
let result = call icfs.test_read_exact();
26+
assert result == null;
27+
28+
let result = call icfs.test_seek_past_end();
29+
assert result == null;
30+
31+
let result = call icfs.test_seek_before_0();
32+
assert result == null;

0 commit comments

Comments
 (0)