Skip to content

Commit

Permalink
chore: add tests for sync api of wasi http components
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardomourar committed Aug 26, 2023
1 parent d9e3a44 commit d1b8af2
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 1 deletion.
2 changes: 1 addition & 1 deletion crates/test-programs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ cap-std = { workspace = true }
cap-rand = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }

wasmtime-wasi-http = { workspace = true }
wasmtime-wasi-http = { workspace = true, features = ["sync"] }

[features]
test_programs = []
46 changes: 46 additions & 0 deletions crates/test-programs/src/http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,29 @@ pub async fn setup_http1(f: impl Future<Output = anyhow::Result<()>>) -> anyhow:
result
}

pub fn setup_http1_sync<F>(f: F) -> anyhow::Result<()>
where
F: FnOnce() -> anyhow::Result<()> + Send + 'static,
{
tracing::debug!("preparing http1 server synchronously");
let server = ServerHttp1::new();

let (tx, rx) = mpsc::channel::<anyhow::Result<()>>();
tracing::debug!("running inner function in a dedicated thread");
std::thread::spawn(move || {
tx.send(f())
.expect("value sent from http1 server dedicated thread");
});
let result = rx
.recv()
.expect("value received from request dedicated thread");

if let Err(err) = server.shutdown() {
tracing::error!("[host/server] failure {:?}", err);
}
result
}

pub async fn setup_http2(f: impl Future<Output = anyhow::Result<()>>) -> anyhow::Result<()> {
tracing::debug!("preparing http2 server asynchronously");
let server = ServerHttp2::new();
Expand All @@ -187,3 +210,26 @@ pub async fn setup_http2(f: impl Future<Output = anyhow::Result<()>>) -> anyhow:
}
result
}

pub fn setup_http2_sync<F>(f: F) -> anyhow::Result<()>
where
F: FnOnce() -> anyhow::Result<()> + Send + 'static,
{
tracing::debug!("preparing http2 server synchronously");
let server = ServerHttp2::new();

let (tx, rx) = mpsc::channel::<anyhow::Result<()>>();
tracing::debug!("running inner function in a dedicated thread");
std::thread::spawn(move || {
tx.send(f())
.expect("value sent from http2 server dedicated thread");
});
let result = rx
.recv()
.expect("value received from request dedicated thread");

if let Err(err) = server.shutdown() {
tracing::error!("[host/server] failure {:?}", err);
}
result
}
160 changes: 160 additions & 0 deletions crates/test-programs/tests/wasi-http-components-sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#![cfg(all(feature = "test_programs", not(skip_wasi_http_tests)))]
use wasmtime::{
component::{Component, Linker},
Config, Engine, Store,
};
use wasmtime_wasi::preview2::{
command::sync::{add_to_linker, Command},
pipe::MemoryOutputPipe,
IsATTY, Table, WasiCtx, WasiCtxBuilder, WasiView,
};
use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView};

use test_programs::http_server::{setup_http1_sync, setup_http2_sync};

lazy_static::lazy_static! {
static ref ENGINE: Engine = {
let mut config = Config::new();
config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable);
config.wasm_component_model(true);
let engine = Engine::new(&config).unwrap();
engine
};
}
// uses ENGINE, creates a fn get_module(&str) -> Module
include!(concat!(env!("OUT_DIR"), "/wasi_http_tests_components.rs"));

struct Ctx {
table: Table,
wasi: WasiCtx,
http: WasiHttpCtx,
}

impl WasiView for Ctx {
fn table(&self) -> &Table {
&self.table
}
fn table_mut(&mut self) -> &mut Table {
&mut self.table
}
fn ctx(&self) -> &WasiCtx {
&self.wasi
}
fn ctx_mut(&mut self) -> &mut WasiCtx {
&mut self.wasi
}
}

impl WasiHttpView for Ctx {
fn http_ctx(&self) -> &WasiHttpCtx {
&self.http
}
fn http_ctx_mut(&mut self) -> &mut WasiHttpCtx {
&mut self.http
}
}

fn instantiate_component(
component: Component,
ctx: Ctx,
) -> Result<(Store<Ctx>, Command), anyhow::Error> {
let mut linker = Linker::new(&ENGINE);
add_to_linker(&mut linker)?;
wasmtime_wasi_http::proxy::sync::add_to_linker(&mut linker)?;

let mut store = Store::new(&ENGINE, ctx);

let (command, _instance) = Command::instantiate(&mut store, &component, &linker)?;
Ok((store, command))
}

fn run(name: &str) -> anyhow::Result<()> {
let stdout = MemoryOutputPipe::new();
let stderr = MemoryOutputPipe::new();
let r = {
let mut table = Table::new();
let component = get_component(name);

// Create our wasi context.
let mut builder = WasiCtxBuilder::new();
builder.stdout(stdout.clone(), IsATTY::No);
builder.stderr(stderr.clone(), IsATTY::No);
builder.arg(name);
for (var, val) in test_programs::wasi_tests_environment() {
builder.env(var, val);
}
let wasi = builder.build(&mut table)?;
let http = WasiHttpCtx::new();

let (mut store, command) = instantiate_component(component, Ctx { table, wasi, http })?;
command
.wasi_cli_run()
.call_run(&mut store)?
.map_err(|()| anyhow::anyhow!("run returned a failure"))?;
Ok(())
};
r.map_err(move |trap: anyhow::Error| {
let stdout = stdout.try_into_inner().expect("single ref to stdout");
if !stdout.is_empty() {
println!("[guest] stdout:\n{}\n===", String::from_utf8_lossy(&stdout));
}
let stderr = stderr.try_into_inner().expect("single ref to stderr");
if !stderr.is_empty() {
println!("[guest] stderr:\n{}\n===", String::from_utf8_lossy(&stderr));
}
trap.context(format!(
"error while testing wasi-tests {} with http-components-sync",
name
))
})?;
Ok(())
}

#[test_log::test]
fn outbound_request_get() {
setup_http1_sync(|| run("outbound_request_get")).unwrap();
}

#[test_log::test]
#[ignore = "test is currently flaky in ci and needs to be debugged"]
fn outbound_request_post() {
setup_http1_sync(|| run("outbound_request_post")).unwrap();
}

#[test_log::test]
fn outbound_request_put() {
setup_http1_sync(|| run("outbound_request_put")).unwrap();
}

#[test_log::test]
#[cfg_attr(
windows,
ignore = "test is currently flaky in ci and needs to be debugged"
)]
fn outbound_request_invalid_version() {
setup_http2_sync(|| run("outbound_request_invalid_version")).unwrap();
}

#[test_log::test]
fn outbound_request_unknown_method() {
run("outbound_request_unknown_method").unwrap();
}

#[test_log::test]
fn outbound_request_unsupported_scheme() {
run("outbound_request_unsupported_scheme").unwrap();
}

#[test_log::test]
fn outbound_request_invalid_port() {
run("outbound_request_invalid_port").unwrap();
}

#[test_log::test]
#[cfg_attr(
windows,
ignore = "test is currently flaky in ci and needs to be debugged"
)]
fn outbound_request_invalid_dnsname() {
run("outbound_request_invalid_dnsname").unwrap();
}

0 comments on commit d1b8af2

Please sign in to comment.