|
@@ -72,55 +72,117 @@ async fn main() {
|
|
|
mod tests {
|
|
|
use super::*;
|
|
|
|
|
|
+ use btlib::log::BuilderExt;
|
|
|
use btfproto::{client::FsClient, msg::*};
|
|
|
use btlib_tests::TpmCredStoreHarness;
|
|
|
- use std::future::ready;
|
|
|
+ use btmsg::Transmitter;
|
|
|
+ use std::{future::ready, net::Ipv4Addr};
|
|
|
use tempdir::TempDir;
|
|
|
|
|
|
- struct TestCase {
|
|
|
- _ip_addr: IpAddr,
|
|
|
+ const LOG_LEVEL: &str = "warn";
|
|
|
+
|
|
|
+ #[ctor::ctor]
|
|
|
+ fn ctor() {
|
|
|
+ std::env::set_var("RUST_LOG", LOG_LEVEL);
|
|
|
+ env_logger::Builder::from_default_env().btformat().init();
|
|
|
+ }
|
|
|
+
|
|
|
+ struct TestCase<R, T> {
|
|
|
+ client: FsClient<T>,
|
|
|
+ rx: R,
|
|
|
_harness: TpmCredStoreHarness,
|
|
|
_dir: TempDir,
|
|
|
}
|
|
|
|
|
|
- impl TestCase {
|
|
|
- const ROOT_PASSWD: &str = "existential_threat";
|
|
|
-
|
|
|
- fn new() -> (Self, impl Receiver) {
|
|
|
- let dir = TempDir::new("btfsd").unwrap();
|
|
|
- let harness = TpmCredStoreHarness::new(Self::ROOT_PASSWD.to_owned()).unwrap();
|
|
|
- let ip_addr = IpAddr::V6(Ipv6Addr::LOCALHOST);
|
|
|
- let config = Config {
|
|
|
- ip_addr,
|
|
|
- tabrmd: harness.swtpm().tabrmd_config().to_owned(),
|
|
|
- tpm_state_path: harness.swtpm().state_path().to_owned(),
|
|
|
- block_dir: dir.path().join("bt"),
|
|
|
- };
|
|
|
- let receiver = receiver(config);
|
|
|
- (
|
|
|
- Self {
|
|
|
- _dir: dir,
|
|
|
- _harness: harness,
|
|
|
- _ip_addr: ip_addr,
|
|
|
- },
|
|
|
- receiver,
|
|
|
- )
|
|
|
+ const ROOT_PASSWD: &str = "existential_threat";
|
|
|
+ const LOCALHOST: IpAddr = IpAddr::V6(Ipv6Addr::LOCALHOST);
|
|
|
+
|
|
|
+ async fn test_case(
|
|
|
+ dir: TempDir,
|
|
|
+ harness: TpmCredStoreHarness,
|
|
|
+ ip_addr: IpAddr,
|
|
|
+ ) -> TestCase<impl Receiver, impl Transmitter> {
|
|
|
+ let config = Config {
|
|
|
+ ip_addr,
|
|
|
+ tabrmd: harness.swtpm().tabrmd_config().to_owned(),
|
|
|
+ tpm_state_path: harness.swtpm().state_path().to_owned(),
|
|
|
+ block_dir: dir.path().join("bt"),
|
|
|
+ };
|
|
|
+ let rx = receiver(config);
|
|
|
+ let tx = rx.transmitter(rx.addr().clone()).await.unwrap();
|
|
|
+ let client = FsClient::new(tx);
|
|
|
+ TestCase {
|
|
|
+ _dir: dir,
|
|
|
+ _harness: harness,
|
|
|
+ rx,
|
|
|
+ client,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ async fn new_case() -> TestCase<impl Receiver, impl Transmitter> {
|
|
|
+ let dir = TempDir::new("btfsd").unwrap();
|
|
|
+ let harness = TpmCredStoreHarness::new(ROOT_PASSWD.to_owned()).unwrap();
|
|
|
+ test_case(dir, harness, LOCALHOST).await
|
|
|
+ }
|
|
|
+
|
|
|
+ async fn existing_case<R: Receiver, T: Transmitter>(
|
|
|
+ case: TestCase<R, T>,
|
|
|
+ ) -> TestCase<impl Receiver, impl Transmitter> {
|
|
|
+ case.rx.stop().await.unwrap();
|
|
|
+ case.rx.complete().unwrap().await.unwrap();
|
|
|
+ let TestCase { _dir, _harness, .. } = case;
|
|
|
+ test_case(_dir, _harness, IpAddr::V4(Ipv4Addr::LOCALHOST)).await
|
|
|
+ }
|
|
|
+
|
|
|
#[allow(dead_code)]
|
|
|
async fn manual_test() {
|
|
|
- let (_case, rx) = TestCase::new();
|
|
|
- rx.complete().unwrap().await.unwrap();
|
|
|
+ let case = new_case().await;
|
|
|
+ case.rx.complete().unwrap().await.unwrap();
|
|
|
}
|
|
|
|
|
|
#[tokio::test]
|
|
|
async fn create_write_read() {
|
|
|
const FILENAME: &str = "file.txt";
|
|
|
const EXPECTED: &[u8] = b"potato";
|
|
|
- let (_case, rx) = TestCase::new();
|
|
|
- let tx = rx.transmitter(rx.addr().clone()).await.unwrap();
|
|
|
- let client = FsClient::new(tx);
|
|
|
+ let case = new_case().await;
|
|
|
+ let client = case.client;
|
|
|
+
|
|
|
+ let CreateReply { inode, handle, .. } = client
|
|
|
+ .create(
|
|
|
+ SpecInodes::RootDir.into(),
|
|
|
+ FILENAME,
|
|
|
+ FlagValue::ReadWrite.into(),
|
|
|
+ 0o644,
|
|
|
+ 0,
|
|
|
+ )
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ let WriteReply { written, .. } = client.write(inode, handle, 0, EXPECTED).await.unwrap();
|
|
|
+ assert_eq!(EXPECTED.len() as u64, written);
|
|
|
+ let msg = Read {
|
|
|
+ inode,
|
|
|
+ handle,
|
|
|
+ offset: 0,
|
|
|
+ size: EXPECTED.len() as u64,
|
|
|
+ };
|
|
|
+ let actual = client
|
|
|
+ .read(msg, |reply| {
|
|
|
+ let mut buf = Vec::with_capacity(EXPECTED.len());
|
|
|
+ buf.extend_from_slice(reply.data);
|
|
|
+ ready(buf)
|
|
|
+ })
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ assert_eq!(EXPECTED, &actual);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn read_from_different_instance() {
|
|
|
+ const FILENAME: &str = "file.txt";
|
|
|
+ const EXPECTED: &[u8] = b"potato";
|
|
|
+ let case = new_case().await;
|
|
|
+ let client = &case.client;
|
|
|
|
|
|
let CreateReply { inode, handle, .. } = client
|
|
|
.create(
|
|
@@ -134,6 +196,14 @@ mod tests {
|
|
|
.unwrap();
|
|
|
let WriteReply { written, .. } = client.write(inode, handle, 0, EXPECTED).await.unwrap();
|
|
|
assert_eq!(EXPECTED.len() as u64, written);
|
|
|
+ client.flush(inode, handle).await.unwrap();
|
|
|
+ let case = existing_case(case).await;
|
|
|
+ let client = &case.client;
|
|
|
+ let LookupReply { inode, .. } = client.lookup(SpecInodes::RootDir.into(), FILENAME).await.unwrap();
|
|
|
+ let OpenReply { handle, .. } = client
|
|
|
+ .open(inode, FlagValue::ReadOnly.into())
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
let msg = Read {
|
|
|
inode,
|
|
|
handle,
|
|
@@ -151,4 +221,128 @@ mod tests {
|
|
|
|
|
|
assert_eq!(EXPECTED, &actual);
|
|
|
}
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn create_lookup() {
|
|
|
+ const FILENAME: &str = "file.txt";
|
|
|
+ let case = new_case().await;
|
|
|
+ let client = case.client;
|
|
|
+
|
|
|
+ let CreateReply {
|
|
|
+ inode: expected, ..
|
|
|
+ } = client
|
|
|
+ .create(
|
|
|
+ SpecInodes::RootDir.into(),
|
|
|
+ FILENAME,
|
|
|
+ FlagValue::ReadOnly.into(),
|
|
|
+ 0o644,
|
|
|
+ 0,
|
|
|
+ )
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ let LookupReply { inode: actual, .. } = client
|
|
|
+ .lookup(SpecInodes::RootDir.into(), FILENAME)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ assert_eq!(expected, actual);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn open_existing() {
|
|
|
+ const FILENAME: &str = "file.txt";
|
|
|
+ let case = new_case().await;
|
|
|
+ let client = case.client;
|
|
|
+
|
|
|
+ let CreateReply { inode, .. } = client
|
|
|
+ .create(
|
|
|
+ SpecInodes::RootDir.into(),
|
|
|
+ FILENAME,
|
|
|
+ FlagValue::ReadOnly.into(),
|
|
|
+ 0o644,
|
|
|
+ 0,
|
|
|
+ )
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ let result = client.open(inode, FlagValue::ReadWrite.into()).await;
|
|
|
+
|
|
|
+ assert!(result.is_ok());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn write_flush_close_read() {
|
|
|
+ const FILENAME: &str = "lyrics.txt";
|
|
|
+ const EXPECTED: &[u8] = b"Fate, or something better";
|
|
|
+ let case = new_case().await;
|
|
|
+ let client = &case.client;
|
|
|
+
|
|
|
+ let CreateReply { inode, handle, .. } = client
|
|
|
+ .create(
|
|
|
+ SpecInodes::RootDir.into(),
|
|
|
+ FILENAME,
|
|
|
+ FlagValue::ReadWrite.into(),
|
|
|
+ 0o644,
|
|
|
+ 0,
|
|
|
+ )
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ let WriteReply { written, .. } = client.write(inode, handle, 0, EXPECTED).await.unwrap();
|
|
|
+ assert_eq!(EXPECTED.len() as u64, written);
|
|
|
+ client.flush(inode, handle).await.unwrap();
|
|
|
+ client.close(inode, handle).await.unwrap();
|
|
|
+ let OpenReply { handle, .. } = client
|
|
|
+ .open(inode, FlagValue::ReadOnly.into())
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ let msg = Read {
|
|
|
+ inode,
|
|
|
+ handle,
|
|
|
+ offset: 0,
|
|
|
+ size: EXPECTED.len() as u64,
|
|
|
+ };
|
|
|
+ let actual = client
|
|
|
+ .read(msg, |reply| ready(reply.data.to_owned()))
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ assert_eq!(EXPECTED, &actual);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[tokio::test]
|
|
|
+ async fn link() {
|
|
|
+ const FIRSTNAME: &str = "Jean-Luc";
|
|
|
+ const LASTNAME: &str = "Picard";
|
|
|
+ let case = new_case().await;
|
|
|
+ let client = &case.client;
|
|
|
+
|
|
|
+ let CreateReply { inode, .. } = client
|
|
|
+ .create(
|
|
|
+ SpecInodes::RootDir.into(),
|
|
|
+ FIRSTNAME,
|
|
|
+ Flags::default(),
|
|
|
+ 0o644,
|
|
|
+ 0,
|
|
|
+ )
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ client
|
|
|
+ .link(inode, SpecInodes::RootDir.into(), LASTNAME)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ let OpenReply {
|
|
|
+ handle: root_handle,
|
|
|
+ ..
|
|
|
+ } = client
|
|
|
+ .open(SpecInodes::RootDir.into(), FlagValue::ReadOnly | FlagValue::Directory)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+ let ReadDirReply { entries, .. } = client
|
|
|
+ .read_dir(SpecInodes::RootDir.into(), root_handle, 0, 0)
|
|
|
+ .await
|
|
|
+ .unwrap();
|
|
|
+
|
|
|
+ let filenames: Vec<_> = entries.iter().map(|e| e.0.as_str()).collect();
|
|
|
+ assert!(filenames.contains(&FIRSTNAME));
|
|
|
+ assert!(filenames.contains(&LASTNAME));
|
|
|
+ }
|
|
|
}
|