Skip to content

Commit cad6aed

Browse files
andrey-zelenkovavahahn
authored andcommitted
Tests: initial "wasm-wasi-component" test
1 parent 593564f commit cad6aed

File tree

9 files changed

+283
-1
lines changed

9 files changed

+283
-1
lines changed

test/test_wasm_component.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import pytest
2+
from unit.applications.lang.wasm_component import ApplicationWasmComponent
3+
4+
prerequisites = {
5+
'modules': {'wasm-wasi-component': 'any'},
6+
'features': {'cargo_component': True},
7+
}
8+
9+
client = ApplicationWasmComponent()
10+
11+
12+
def test_wasm_component():
13+
client.load('hello_world')
14+
15+
req = client.get()
16+
17+
assert client.get()['status'] == 200
18+
assert req['body'] == 'Hello'
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from pathlib import Path
2+
import shutil
3+
import subprocess
4+
from urllib.parse import quote
5+
6+
from unit.applications.proto import ApplicationProto
7+
from unit.option import option
8+
9+
10+
class ApplicationWasmComponent(ApplicationProto):
11+
@staticmethod
12+
def prepare_env(script):
13+
try:
14+
subprocess.check_output(['cargo', 'component', '--help'])
15+
except (subprocess.CalledProcessError, FileNotFoundError):
16+
return None
17+
18+
temp_dir = Path(f'{option.temp_dir}/wasm_component/')
19+
20+
if not temp_dir.exists():
21+
temp_dir.mkdir()
22+
23+
app_path = f'{temp_dir}/{script}'
24+
25+
shutil.copytree(f'{option.test_dir}/wasm_component/{script}', app_path)
26+
27+
try:
28+
output = subprocess.check_output(
29+
['cargo', 'component', 'build', '--release'],
30+
cwd=app_path,
31+
stderr=subprocess.STDOUT,
32+
)
33+
except KeyboardInterrupt:
34+
raise
35+
36+
except subprocess.CalledProcessError:
37+
return None
38+
39+
return output
40+
41+
def load(self, script, **kwargs):
42+
self.prepare_env(script)
43+
44+
component_path = f'{option.temp_dir}/wasm_component/{script}/target/wasm32-wasi/release/test_wasi_component.wasm'
45+
46+
self._load_conf(
47+
{
48+
"listeners": {
49+
"*:8080": {"pass": f"applications/{quote(script, '')}"}
50+
},
51+
"applications": {
52+
script: {
53+
"type": "wasm-wasi-component",
54+
"processes": {"spare": 0},
55+
"component": component_path,
56+
}
57+
},
58+
},
59+
**kwargs,
60+
)

test/unit/check/cargo_component.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from unit.applications.lang.wasm_component import ApplicationWasmComponent
2+
3+
def check_cargo_component():
4+
return ApplicationWasmComponent.prepare_env('hello_world') is not None

test/unit/check/discover_available.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import subprocess
22
import sys
33

4+
from unit.check.cargo_component import check_cargo_component
45
from unit.check.chroot import check_chroot
56
from unit.check.go import check_go
67
from unit.check.isolation import check_isolation
@@ -28,7 +29,7 @@ def discover_available(unit):
2829

2930
# discover modules from log file
3031

31-
for module in Log.findall(r'module: ([a-zA-Z]+) (.*) ".*"$'):
32+
for module in Log.findall(r'module: ([a-zA-Z\-]+) (.*) ".*"$'):
3233
versions = option.available['modules'].setdefault(module[0], [])
3334
if module[1] not in versions:
3435
versions.append(module[1])
@@ -44,6 +45,7 @@ def discover_available(unit):
4445
# Discover features using check. Features should be discovered after
4546
# modules since some features can require modules.
4647

48+
option.available['features']['cargo_component'] = check_cargo_component()
4749
option.available['features']['chroot'] = check_chroot()
4850
option.available['features']['isolation'] = check_isolation()
4951
option.available['features']['unix_abstract'] = check_unix_abstract()

test/wasm_component/hello_world/Cargo.lock

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "test-wasi-component"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
bitflags = "2.4.2"
8+
wit-bindgen-rt = "0.21.0"
9+
wasi = "0.13.0"
10+
11+
[lib]
12+
crate-type = ["cdylib"]
13+
14+
[package.metadata.component]
15+
package = "component:test-wasi-component"
16+
proxy = true
17+
18+
[package.metadata.component.dependencies]
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Generated by `wit-bindgen` 0.24.0. DO NOT EDIT!
2+
// Options used:
3+
#[doc(hidden)]
4+
#[allow(non_snake_case)]
5+
pub unsafe fn _export_hello_world_cabi<T: Guest>() -> *mut u8 {
6+
#[cfg(target_arch = "wasm32")]
7+
_rt::run_ctors_once();
8+
let result0 = T::hello_world();
9+
let ptr1 = _RET_AREA.0.as_mut_ptr().cast::<u8>();
10+
let vec2 = (result0.into_bytes()).into_boxed_slice();
11+
let ptr2 = vec2.as_ptr().cast::<u8>();
12+
let len2 = vec2.len();
13+
::core::mem::forget(vec2);
14+
*ptr1.add(4).cast::<usize>() = len2;
15+
*ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut();
16+
ptr1
17+
}
18+
#[doc(hidden)]
19+
#[allow(non_snake_case)]
20+
pub unsafe fn __post_return_hello_world<T: Guest>(arg0: *mut u8) {
21+
let l0 = *arg0.add(0).cast::<*mut u8>();
22+
let l1 = *arg0.add(4).cast::<usize>();
23+
_rt::cabi_dealloc(l0, l1, 1);
24+
}
25+
pub trait Guest {
26+
fn hello_world() -> _rt::String;
27+
}
28+
#[doc(hidden)]
29+
30+
macro_rules! __export_world_example_cabi{
31+
($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {
32+
33+
#[export_name = "hello-world"]
34+
unsafe extern "C" fn export_hello_world() -> *mut u8 {
35+
$($path_to_types)*::_export_hello_world_cabi::<$ty>()
36+
}
37+
#[export_name = "cabi_post_hello-world"]
38+
unsafe extern "C" fn _post_return_hello_world(arg0: *mut u8,) {
39+
$($path_to_types)*::__post_return_hello_world::<$ty>(arg0)
40+
}
41+
};);
42+
}
43+
#[doc(hidden)]
44+
pub(crate) use __export_world_example_cabi;
45+
#[repr(align(4))]
46+
struct _RetArea([::core::mem::MaybeUninit<u8>; 8]);
47+
static mut _RET_AREA: _RetArea =
48+
_RetArea([::core::mem::MaybeUninit::uninit(); 8]);
49+
mod _rt {
50+
51+
#[cfg(target_arch = "wasm32")]
52+
pub fn run_ctors_once() {
53+
wit_bindgen_rt::run_ctors_once();
54+
}
55+
pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) {
56+
if size == 0 {
57+
return;
58+
}
59+
let layout = alloc::Layout::from_size_align_unchecked(size, align);
60+
alloc::dealloc(ptr as *mut u8, layout);
61+
}
62+
pub use alloc_crate::alloc;
63+
pub use alloc_crate::string::String;
64+
extern crate alloc as alloc_crate;
65+
}
66+
67+
/// Generates `#[no_mangle]` functions to export the specified type as the
68+
/// root implementation of all generated traits.
69+
///
70+
/// For more information see the documentation of `wit_bindgen::generate!`.
71+
///
72+
/// ```rust
73+
/// # macro_rules! export{ ($($t:tt)*) => (); }
74+
/// # trait Guest {}
75+
/// struct MyType;
76+
///
77+
/// impl Guest for MyType {
78+
/// // ...
79+
/// }
80+
///
81+
/// export!(MyType);
82+
/// ```
83+
#[allow(unused_macros)]
84+
#[doc(hidden)]
85+
86+
macro_rules! __export_example_impl {
87+
($ty:ident) => (self::export!($ty with_types_in self););
88+
($ty:ident with_types_in $($path_to_types_root:tt)*) => (
89+
$($path_to_types_root)*::__export_world_example_cabi!($ty with_types_in $($path_to_types_root)*);
90+
)
91+
}
92+
#[doc(inline)]
93+
pub(crate) use __export_example_impl as export;
94+
95+
#[cfg(target_arch = "wasm32")]
96+
#[link_section = "component-type:wit-bindgen:0.24.0:example:encoded world"]
97+
#[doc(hidden)]
98+
pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 194] = *b"\
99+
\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07E\x01A\x02\x01A\x02\x01\
100+
@\0\0s\x04\0\x0bhello-world\x01\0\x04\x01%component:test-wasi-component/example\x04\
101+
\0\x0b\x0d\x01\0\x07example\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dw\
102+
it-component\x070.202.0\x10wit-bindgen-rust\x060.24.0";
103+
104+
#[inline(never)]
105+
#[doc(hidden)]
106+
#[cfg(target_arch = "wasm32")]
107+
pub fn __link_custom_section_describing_imports() {
108+
wit_bindgen_rt::maybe_link_cabi_realloc();
109+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use wasi::http::types::{
2+
Fields, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam,
3+
};
4+
5+
wasi::http::proxy::export!(Component);
6+
7+
struct Component;
8+
9+
impl wasi::exports::http::incoming_handler::Guest for Component {
10+
fn handle(_request: IncomingRequest, response_out: ResponseOutparam) {
11+
12+
let hdrs = Fields::new();
13+
let mesg = String::from("Hello");
14+
let _try = hdrs.set(&"Content-Type".to_string(), &[b"plain/text".to_vec()]);
15+
let _try = hdrs.set(&"Content-Length".to_string(), &[mesg.len().to_string().as_bytes().to_vec()]);
16+
17+
let resp = OutgoingResponse::new(hdrs);
18+
19+
// Add the HTTP Response Status Code
20+
resp.set_status_code(200).unwrap();
21+
22+
let body = resp.body().unwrap();
23+
ResponseOutparam::set(response_out, Ok(resp));
24+
25+
let out = body.write().unwrap();
26+
out.blocking_write_and_flush(mesg.as_bytes()).unwrap();
27+
drop(out);
28+
29+
OutgoingBody::finish(body, None).unwrap();
30+
}
31+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package component:test-wasi-component;
2+
3+
/// An example world for the component to target.
4+
world example {
5+
export hello-world: func() -> string;
6+
}

0 commit comments

Comments
 (0)