Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions crates/test-programs/src/bin/p3_path_link.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
use test_programs::p3::wasi;
use test_programs::p3::wasi::filesystem::types::{
Descriptor, DescriptorFlags, ErrorCode, OpenFlags, PathFlags,
};

struct Component;

test_programs::p3::export!(Component);

impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
async fn run() -> Result<(), ()> {
let preopens = wasi::filesystem::preopens::get_directories();
let (dir, _) = &preopens[0];

test_path_link(dir).await;
Ok(())
}
}

fn main() {
unreachable!()
}

async fn test_path_link(dir: &Descriptor) {
// Create a file
dir.open_at(
PathFlags::empty(),
"file".to_string(),
OpenFlags::CREATE,
DescriptorFlags::empty(),
)
.await
.expect("create file");

// Open a fresh descriptor to the file
let file = dir
.open_at(
PathFlags::empty(),
"file".to_string(),
OpenFlags::empty(),
DescriptorFlags::READ,
)
.await
.expect("open file");

// Create a link in the same directory and verify they refer to the same object
dir.link_at(
PathFlags::empty(),
"file".to_string(),
dir,
"link".to_string(),
)
.await
.expect("creating a link in the same directory");

let link = dir
.open_at(
PathFlags::empty(),
"link".to_string(),
OpenFlags::empty(),
DescriptorFlags::READ,
)
.await
.expect("open link");

assert!(
file.is_same_object(&link).await,
"file and link should be the same object"
);
let file_hash = file.metadata_hash().await.expect("file metadata hash");
let link_hash = link.metadata_hash().await.expect("link metadata hash");
assert_eq!(file_hash.lower, link_hash.lower, "metadata hash lower should be equal");
assert_eq!(file_hash.upper, link_hash.upper, "metadata hash upper should be equal");

drop(link);
dir.unlink_file_at("link".to_string())
.await
.expect("removing a link");

// Create a link in a different directory and verify they refer to the same object
dir.create_directory_at("subdir".to_string())
.await
.expect("creating a subdirectory");
let subdir = dir
.open_at(
PathFlags::empty(),
"subdir".to_string(),
OpenFlags::DIRECTORY,
DescriptorFlags::MUTATE_DIRECTORY | DescriptorFlags::READ,
)
.await
.expect("open subdir directory");

dir.link_at(
PathFlags::empty(),
"file".to_string(),
&subdir,
"link".to_string(),
)
.await
.expect("creating a link in subdirectory");

let link = subdir
.open_at(
PathFlags::empty(),
"link".to_string(),
OpenFlags::empty(),
DescriptorFlags::READ,
)
.await
.expect("open link in subdir");

assert!(
file.is_same_object(&link).await,
"file and link in subdir should be the same object"
);

drop(link);
subdir
.unlink_file_at("link".to_string())
.await
.expect("removing a link");
drop(subdir);
dir.remove_directory_at("subdir".to_string())
.await
.expect("removing a subdirectory");

// Create a link to a path that already exists
dir.open_at(
PathFlags::empty(),
"link".to_string(),
OpenFlags::CREATE,
DescriptorFlags::empty(),
)
.await
.expect("create link file");

let err = dir
.link_at(
PathFlags::empty(),
"file".to_string(),
dir,
"link".to_string(),
)
.await
.expect_err("creating a link to existing path should fail");
assert!(
matches!(err, ErrorCode::Exist),
"expected Exist error, got {err:?}"
);
dir.unlink_file_at("link".to_string())
.await
.expect("removing a file");

// Create a link to itself
let err = dir
.link_at(
PathFlags::empty(),
"file".to_string(),
dir,
"file".to_string(),
)
.await
.expect_err("creating a link to itself should fail");
assert!(
matches!(err, ErrorCode::Exist),
"expected Exist error, got {err:?}"
);

// Create a link where target is a directory
dir.create_directory_at("link".to_string())
.await
.expect("creating a dir");

let err = dir
.link_at(
PathFlags::empty(),
"file".to_string(),
dir,
"link".to_string(),
)
.await
.expect_err("creating a link where target is a directory should fail");
assert!(
matches!(err, ErrorCode::Exist),
"expected Exist error, got {err:?}"
);
dir.remove_directory_at("link".to_string())
.await
.expect("removing a dir");

// Create a link to a directory
dir.create_directory_at("subdir".to_string())
.await
.expect("creating a subdirectory");

let err = dir
.link_at(
PathFlags::empty(),
"subdir".to_string(),
dir,
"link".to_string(),
)
.await
.expect_err("creating a link to a directory should fail");
assert!(
matches!(err, ErrorCode::NotPermitted | ErrorCode::Access),
"expected NotPermitted or Access error, got {err:?}"
);
dir.remove_directory_at("subdir".to_string())
.await
.expect("removing a subdirectory");

// Create a link to a file with trailing slash
let err = dir
.link_at(
PathFlags::empty(),
"file".to_string(),
dir,
"link/".to_string(),
)
.await
.expect_err("creating a link to a file with trailing slash should fail");
assert!(
matches!(err, ErrorCode::NoEntry),
"expected NoEntry error, got {err:?}"
);

// Clean up
drop(file);
dir.unlink_file_at("file".to_string())
.await
.expect("removing a file");
}
10 changes: 10 additions & 0 deletions crates/wasi/tests/all/p3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,13 @@ async fn p3_file_write() -> wasmtime::Result<()> {
async fn p3_file_write_blocking() -> wasmtime::Result<()> {
run_allow_blocking_current_thread(P3_FILE_WRITE_COMPONENT, true).await
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn p3_path_link() -> wasmtime::Result<()> {
run(P3_PATH_LINK_COMPONENT).await
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn p3_path_link_blocking() -> wasmtime::Result<()> {
run_allow_blocking_current_thread(P3_PATH_LINK_COMPONENT, true).await
}
Comment on lines +173 to +176
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this is fine to omit, most of the tests here don't have this variant and IIRC for the above two (or maybe just readdir?) it was a specific edge case that was being tested. Or at least I think, maybe I'm misremembering...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have a deeper look. Thanks for thinking about this.

Loading