Skip to content

Commit

Permalink
feat: implement wasip3
Browse files Browse the repository at this point in the history
Signed-off-by: Roman Volosatovs <[email protected]>
  • Loading branch information
rvolosatovs committed Feb 5, 2025
1 parent d6e14f6 commit 5c4f359
Show file tree
Hide file tree
Showing 62 changed files with 5,436 additions and 3 deletions.
8 changes: 8 additions & 0 deletions ci/vendor-wit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ make_vendor "wasi-config" "config@f4d699b"

make_vendor "wasi-keyvalue" "keyvalue@219ea36"

make_vendor "wasi/src/p3" "
cli@[email protected]
clocks@[email protected]
filesystem@[email protected]
random@[email protected]
sockets@[email protected]
"

rm -rf $cache_dir

# Separately (for now), vendor the `wasi-nn` WIT files since their retrieval is
Expand Down
10 changes: 10 additions & 0 deletions crates/test-programs/artifacts/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ fn build_and_generate_tests() {
// Bucket, based on the name of the test, into a "kind" which generates
// a `foreach_*` macro below.
let kind = match target.as_str() {
s if s.starts_with("cli_0_3") => "cli_0_3",
s if s.starts_with("clocks_0_3") => "clocks_0_3",
s if s.starts_with("filesystem_0_3") => "filesystem_0_3",
s if s.starts_with("random_0_3") => "random_0_3",
s if s.starts_with("sockets_0_3") => "sockets_0_3",
s if s.starts_with("http_") => "http",
s if s.starts_with("preview1_") => "preview1",
s if s.starts_with("preview2_") => "preview2",
Expand Down Expand Up @@ -102,6 +107,11 @@ fn build_and_generate_tests() {
}
let adapter = match target.as_str() {
"reactor" => &reactor_adapter,
s if s.starts_with("cli_0_3") => &reactor_adapter,
s if s.starts_with("clocks_0_3") => &reactor_adapter,
s if s.starts_with("filesystem_0_3") => &reactor_adapter,
s if s.starts_with("random_0_3") => &reactor_adapter,
s if s.starts_with("sockets_0_3") => &reactor_adapter,
s if s.starts_with("async_") => &reactor_adapter,
s if s.starts_with("api_proxy") => &proxy_adapter,
_ => &command_adapter,
Expand Down
70 changes: 70 additions & 0 deletions crates/test-programs/src/bin/clocks_0_3_sleep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use core::future::Future as _;
use core::pin::pin;
use core::ptr;
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};

use test_programs::p3::wasi::clocks::monotonic_clock;

struct Component;

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

impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
async fn run() -> Result<(), ()> {
sleep_10ms().await;
sleep_0ms();
sleep_backwards_in_time();
Ok(())
}
}

// Adapted from https://github.com/rust-lang/rust/blob/cd805f09ffbfa3896c8f50a619de9b67e1d9f3c3/library/core/src/task/wake.rs#L63-L77
// TODO: Replace by `Waker::noop` once MSRV is raised to 1.85
const NOOP_RAW_WAKER: RawWaker = {
const VTABLE: RawWakerVTable = RawWakerVTable::new(
// Cloning just returns a new no-op raw waker
|_| NOOP_RAW_WAKER,
// `wake` does nothing
|_| {},
// `wake_by_ref` does nothing
|_| {},
// Dropping does nothing as we don't allocate anything
|_| {},
);
RawWaker::new(ptr::null(), &VTABLE)
};

const NOOP_WAKER: &'static Waker = &unsafe { Waker::from_raw(NOOP_RAW_WAKER) };

async fn sleep_10ms() {
let dur = 10_000_000;
monotonic_clock::wait_until(monotonic_clock::now() + dur).await;
monotonic_clock::wait_for(dur).await;
}

fn sleep_0ms() {
let mut cx = Context::from_waker(NOOP_WAKER);

assert_eq!(
pin!(monotonic_clock::wait_until(monotonic_clock::now())).poll(&mut cx),
Poll::Ready(()),
"waiting until now() is ready immediately",
);
assert_eq!(
pin!(monotonic_clock::wait_for(0)).poll(&mut cx),
Poll::Ready(()),
"waiting for 0 is ready immediately",
);
}

fn sleep_backwards_in_time() {
let mut cx = Context::from_waker(NOOP_WAKER);

assert_eq!(
pin!(monotonic_clock::wait_until(monotonic_clock::now() - 1)).poll(&mut cx),
Poll::Ready(()),
"waiting until instant which has passed is ready immediately",
);
}

fn main() {}
49 changes: 49 additions & 0 deletions crates/test-programs/src/bin/random_0_3_imports.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use test_programs::p3::wasi::random;

struct Component;

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

impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
async fn run() -> Result<(), ()> {
let mut bytes = [0_u8; 256];
getrandom::getrandom(&mut bytes).unwrap();

assert!(bytes.iter().any(|x| *x != 0));

// Acquired random bytes should be of the expected length.
let array = random::random::get_random_bytes(100);
assert_eq!(array.len(), 100);

// It shouldn't take 100+ tries to get a nonzero random integer.
for i in 0.. {
if random::random::get_random_u64() == 0 {
continue;
}
assert!(i < 100);
break;
}

// The `insecure_seed` API should return the same result each time.
let (a1, b1) = random::insecure_seed::insecure_seed();
let (a2, b2) = random::insecure_seed::insecure_seed();
assert_eq!(a1, a2);
assert_eq!(b1, b2);

// Acquired random bytes should be of the expected length.
let array = random::insecure::get_insecure_random_bytes(100);
assert_eq!(array.len(), 100);

// It shouldn't take 100+ tries to get a nonzero random integer.
for i in 0.. {
if random::insecure::get_insecure_random_u64() == 0 {
continue;
}
assert!(i < 100);
break;
}
Ok(())
}
}

fn main() {}
94 changes: 94 additions & 0 deletions crates/test-programs/src/bin/sockets_0_3_ip_name_lookup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use futures::try_join;
use test_programs::p3::wasi::sockets::ip_name_lookup::{resolve_addresses, ErrorCode};
use test_programs::p3::wasi::sockets::types::IpAddress;

struct Component;

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

async fn resolve_one(name: &str) -> Result<IpAddress, ErrorCode> {
Ok(resolve_addresses(name).await?.first().unwrap().to_owned())
}

impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
async fn run() -> Result<(), ()> {
// Valid domains
try_join!(
resolve_addresses("localhost"),
resolve_addresses("example.com")
)
.unwrap();

// NB: this is an actual real resolution, so it might time out, might cause
// issues, etc. This result is ignored to prevent flaky failures in CI.
let _ = resolve_addresses("münchen.de").await;

// Valid IP addresses
assert_eq!(
resolve_one("0.0.0.0").await.unwrap(),
IpAddress::IPV4_UNSPECIFIED
);
assert_eq!(
resolve_one("127.0.0.1").await.unwrap(),
IpAddress::IPV4_LOOPBACK
);
assert_eq!(
resolve_one("192.0.2.0").await.unwrap(),
IpAddress::Ipv4((192, 0, 2, 0))
);
assert_eq!(
resolve_one("::").await.unwrap(),
IpAddress::IPV6_UNSPECIFIED
);
assert_eq!(resolve_one("::1").await.unwrap(), IpAddress::IPV6_LOOPBACK);
assert_eq!(
resolve_one("[::]").await.unwrap(),
IpAddress::IPV6_UNSPECIFIED
);
assert_eq!(
resolve_one("2001:0db8:0:0:0:0:0:0").await.unwrap(),
IpAddress::Ipv6((0x2001, 0x0db8, 0, 0, 0, 0, 0, 0))
);
assert_eq!(
resolve_one("dead:beef::").await.unwrap(),
IpAddress::Ipv6((0xdead, 0xbeef, 0, 0, 0, 0, 0, 0))
);
assert_eq!(
resolve_one("dead:beef::0").await.unwrap(),
IpAddress::Ipv6((0xdead, 0xbeef, 0, 0, 0, 0, 0, 0))
);
assert_eq!(
resolve_one("DEAD:BEEF::0").await.unwrap(),
IpAddress::Ipv6((0xdead, 0xbeef, 0, 0, 0, 0, 0, 0))
);

// Invalid inputs
assert_eq!(
resolve_addresses("").await.unwrap_err(),
ErrorCode::InvalidArgument
);
assert_eq!(
resolve_addresses(" ").await.unwrap_err(),
ErrorCode::InvalidArgument
);
assert_eq!(
resolve_addresses("a.b<&>").await.unwrap_err(),
ErrorCode::InvalidArgument
);
assert_eq!(
resolve_addresses("127.0.0.1:80").await.unwrap_err(),
ErrorCode::InvalidArgument
);
assert_eq!(
resolve_addresses("[::]:80").await.unwrap_err(),
ErrorCode::InvalidArgument
);
assert_eq!(
resolve_addresses("http://example.com/").await.unwrap_err(),
ErrorCode::InvalidArgument
);
Ok(())
}
}

fn main() {}
Loading

0 comments on commit 5c4f359

Please sign in to comment.