Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wasi/tests/all/p2/api.rs
3139 views
1
use std::io::Write;
2
use std::sync::Mutex;
3
use std::time::Duration;
4
use wasmtime::Result;
5
use wasmtime::Store;
6
use wasmtime::component::{Component, Linker, ResourceTable};
7
use wasmtime_wasi::p2::add_to_linker_async;
8
use wasmtime_wasi::p2::bindings::{Command, clocks::wall_clock, filesystem::types as filesystem};
9
use wasmtime_wasi::{
10
DirPerms, FilePerms, HostMonotonicClock, HostWallClock, WasiCtx, WasiCtxBuilder, WasiCtxView,
11
WasiView,
12
};
13
14
struct CommandCtx {
15
table: ResourceTable,
16
wasi: WasiCtx,
17
}
18
19
impl WasiView for CommandCtx {
20
fn ctx(&mut self) -> WasiCtxView<'_> {
21
WasiCtxView {
22
ctx: &mut self.wasi,
23
table: &mut self.table,
24
}
25
}
26
}
27
28
use test_programs_artifacts::*;
29
30
foreach_p2_api!(assert_test_exists);
31
32
async fn instantiate(path: &str, ctx: CommandCtx) -> Result<(Store<CommandCtx>, Command)> {
33
let engine = test_programs_artifacts::engine(|_config| {});
34
let mut linker = Linker::new(&engine);
35
add_to_linker_async(&mut linker)?;
36
37
let mut store = Store::new(&engine, ctx);
38
let component = Component::from_file(&engine, path)?;
39
let command = Command::instantiate_async(&mut store, &component, &linker).await?;
40
Ok((store, command))
41
}
42
43
#[test_log::test(tokio::test(flavor = "multi_thread"))]
44
async fn p2_api_time() -> Result<()> {
45
struct FakeWallClock;
46
47
impl HostWallClock for FakeWallClock {
48
fn resolution(&self) -> Duration {
49
Duration::from_secs(1)
50
}
51
52
fn now(&self) -> Duration {
53
Duration::new(1431648000, 100)
54
}
55
}
56
57
struct FakeMonotonicClock {
58
now: Mutex<u64>,
59
}
60
61
impl HostMonotonicClock for FakeMonotonicClock {
62
fn resolution(&self) -> u64 {
63
1_000_000_000
64
}
65
66
fn now(&self) -> u64 {
67
let mut now = self.now.lock().unwrap();
68
let then = *now;
69
*now += 42 * 1_000_000_000;
70
then
71
}
72
}
73
74
let table = ResourceTable::new();
75
let wasi = WasiCtxBuilder::new()
76
.monotonic_clock(FakeMonotonicClock { now: Mutex::new(0) })
77
.wall_clock(FakeWallClock)
78
.build();
79
80
let (mut store, command) =
81
instantiate(P2_API_TIME_COMPONENT, CommandCtx { table, wasi }).await?;
82
83
command
84
.wasi_cli_run()
85
.call_run(&mut store)
86
.await?
87
.map_err(|()| wasmtime::format_err!("command returned with failing exit status"))
88
}
89
90
#[test_log::test(tokio::test(flavor = "multi_thread"))]
91
async fn p2_api_read_only() -> Result<()> {
92
let dir = tempfile::tempdir()?;
93
94
std::fs::File::create(dir.path().join("bar.txt"))?.write_all(b"And stood awhile in thought")?;
95
std::fs::create_dir(dir.path().join("sub"))?;
96
97
let table = ResourceTable::new();
98
let wasi = WasiCtxBuilder::new()
99
.preopened_dir(dir.path(), "/", DirPerms::READ, FilePerms::READ)?
100
.build();
101
102
let (mut store, command) =
103
instantiate(P2_API_READ_ONLY_COMPONENT, CommandCtx { table, wasi }).await?;
104
105
command
106
.wasi_cli_run()
107
.call_run(&mut store)
108
.await?
109
.map_err(|()| wasmtime::format_err!("command returned with failing exit status"))
110
}
111
112
#[expect(
113
dead_code,
114
reason = "tested in the wasi-http crate, satisfying foreach_api! macro"
115
)]
116
fn p2_api_proxy() {}
117
118
#[expect(
119
dead_code,
120
reason = "tested in the wasi-http crate, satisfying foreach_api! macro"
121
)]
122
fn p2_api_proxy_streaming() {}
123
124
#[expect(
125
dead_code,
126
reason = "tested in the wasi-http crate, satisfying foreach_api! macro"
127
)]
128
fn p2_api_proxy_forward_request() {}
129
130
wasmtime::component::bindgen!({
131
path: "src/p2/wit",
132
world: "test-reactor",
133
imports: { default: async },
134
exports: { default: async },
135
require_store_data_send: true,
136
with: { "wasi": wasmtime_wasi::p2::bindings },
137
ownership: Borrowing {
138
duplicate_if_necessary: false
139
}
140
});
141
142
#[test_log::test(tokio::test)]
143
async fn p2_api_reactor() -> Result<()> {
144
let table = ResourceTable::new();
145
let wasi = WasiCtxBuilder::new().env("GOOD_DOG", "gussie").build();
146
let engine = test_programs_artifacts::engine(|_config| {});
147
let mut linker = Linker::new(&engine);
148
add_to_linker_async(&mut linker)?;
149
150
let mut store = Store::new(&engine, CommandCtx { table, wasi });
151
let component = Component::from_file(&engine, P2_API_REACTOR_COMPONENT)?;
152
let reactor = TestReactor::instantiate_async(&mut store, &component, &linker).await?;
153
154
// Show that integration with the WASI context is working - the guest will
155
// interpolate $GOOD_DOG to gussie here using the environment:
156
let r = reactor
157
.call_add_strings(&mut store, &["hello", "$GOOD_DOG"])
158
.await?;
159
assert_eq!(r, 2);
160
161
let contents = reactor.call_get_strings(&mut store).await?;
162
assert_eq!(contents, &["hello", "gussie"]);
163
164
// Show that we can pass in a resource type whose impls are defined in the
165
// `host` and `wasi-common` crate.
166
// Note, this works because of the add_to_linker invocations using the
167
// `host` crate for `streams`, not because of `with` in the bindgen macro.
168
let writepipe = wasmtime_wasi::p2::pipe::MemoryOutputPipe::new(4096);
169
let stream: wasmtime_wasi::p2::DynOutputStream = Box::new(writepipe.clone());
170
let table_ix = store.data_mut().table.push(stream)?;
171
let r = reactor.call_write_strings_to(&mut store, table_ix).await?;
172
assert_eq!(r, Ok(()));
173
174
assert_eq!(writepipe.contents().as_ref(), b"hellogussie");
175
176
// Show that the `with` invocation in the macro means we get to re-use the
177
// type definitions from inside the `host` crate for these structures:
178
let ds = filesystem::DescriptorStat {
179
data_access_timestamp: Some(wall_clock::Datetime {
180
nanoseconds: 123,
181
seconds: 45,
182
}),
183
data_modification_timestamp: Some(wall_clock::Datetime {
184
nanoseconds: 789,
185
seconds: 10,
186
}),
187
link_count: 0,
188
size: 0,
189
status_change_timestamp: Some(wall_clock::Datetime {
190
nanoseconds: 0,
191
seconds: 1,
192
}),
193
type_: filesystem::DescriptorType::Unknown,
194
};
195
let expected = format!("{ds:?}");
196
let got = reactor.call_pass_an_imported_record(&mut store, ds).await?;
197
assert_eq!(expected, got);
198
199
Ok(())
200
}
201
202