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