diff --git a/Cargo.lock b/Cargo.lock index c857d5862..dee7d4d4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1916,9 +1916,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "leb128-tokio" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a1f734b00871cae8f137d83d53cf4b5ee3f0d87224a87a4347dab4cde11a7a3" +checksum = "68e94e2f8143ae63a07c670a27f2fc42a333293c249566f8d4cd6609689656bb" dependencies = [ "tokio", "tokio-util", @@ -4106,9 +4106,9 @@ dependencies = [ [[package]] name = "wasm-tokio" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db8d9df11ba3f83f9a9fb2c159579e6ef7b368fa6650b8f7f01febb75784cef" +checksum = "a89e5174a1312bd31d081a1dd6db8e48e2dc680b2988aba43befabb8459ea999" dependencies = [ "leb128-tokio", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 475649a8f..593f0a94a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -171,7 +171,7 @@ url = { version = "2" } uuid = { version = "1", default-features = false } wasi = { version = "0.14", default-features = false } wasi-preview1-component-adapter-provider = { version = "45", default-features = false } -wasm-tokio = { version = "0.6", default-features = false } +wasm-tokio = { version = "0.6.1", default-features = false } wasm-wave = { version = "0.252", default-features = false } wasmparser = { version = "0.252", default-features = false } wasmtime = { version = "45", default-features = false } diff --git a/crates/wit-bindgen-go/src/interface.rs b/crates/wit-bindgen-go/src/interface.rs index 34f28c557..9f03a8beb 100644 --- a/crates/wit-bindgen-go/src/interface.rs +++ b/crates/wit-bindgen-go/src/interface.rs @@ -3599,6 +3599,15 @@ func (v *{name}) WriteToIndex(w {wrpc}.ByteWriter) (func({wrpc}.IndexWriter) err ); } + // Number of meaningful bits in the final byte. When the flag count + // is an exact multiple of 8 the last byte is fully used, so the + // shift must be 8 (not `len % 8 == 0`, which would reject every + // set bit in that byte as "unassociated"). + let last_byte_bits = if ty.flags.is_empty() { + 0 + } else { + (ty.flags.len() - 1) % 8 + 1 + }; uwriteln!( self.src, r#" @@ -3606,7 +3615,7 @@ func (v *{name}) WriteToIndex(w {wrpc}.ByteWriter) (func({wrpc}.IndexWriter) err return {errors}.New("bit not associated with any flag is set") }}"#, buf_len - 1, - ty.flags.len() % 8, + last_byte_bits, ); self.push_str("return nil\n}\n"); diff --git a/crates/wit-bindgen-rust/src/interface.rs b/crates/wit-bindgen-rust/src/interface.rs index d564d7226..da8ef998a 100644 --- a/crates/wit-bindgen-rust/src/interface.rs +++ b/crates/wit-bindgen-rust/src/interface.rs @@ -811,7 +811,7 @@ pub fn serve_interface<'a, T: {wrpc_transport}::Serve>( if self.in_import { uwrite!( self.src, - "<'a, C: {wrpc_transport}::Invoke>(wrpc__: &'a C, cx__: C::Context,", + "<'a, C__: {wrpc_transport}::Invoke>(wrpc__: &'a C__, cx__: C__::Context,", wrpc_transport = self.r#gen.wrpc_transport_path(), ); } else { diff --git a/crates/wit-bindgen-test/src/go.rs b/crates/wit-bindgen-test/src/go.rs index 8ce3f3e2b..e972c8a5b 100644 --- a/crates/wit-bindgen-test/src/go.rs +++ b/crates/wit-bindgen-test/src/go.rs @@ -1,7 +1,8 @@ -use crate::{LanguageMethods, Runner, Verify}; +use crate::{Component, LanguageMethods, Runner, Test, Verify}; use anyhow::{Context, Result}; use clap::Parser; use std::env; +use std::path::Path; use std::process::Command; #[derive(Default, Debug, Clone, Parser)] @@ -77,3 +78,140 @@ impl LanguageMethods for Go { .context("failed to compile generated Go bindings") } } + +/// The fixed driver linking a `runner` (client) and `test` (server) into one +/// Go program, connected over an in-process TCP transport. Mirrors the Rust +/// driver: it serves the `test` world's exports and runs the `runner` world's +/// `Run` against a client connected to the in-process listener. +const DRIVER: &str = r#"package main + +import ( + "context" + "errors" + "net" + "os" + "time" + + wrpctcp "wrpc.io/go/x/tcp" + + "driver/runner" + "driver/test" +) + +func main() { + a, err := net.ResolveTCPAddr("tcp", "[::1]:0") + if err != nil { + panic(err) + } + l, err := net.ListenTCP("tcp", a) + if err != nil { + panic(err) + } + addr := l.Addr().String() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + srv := wrpctcp.NewServerWithContext(ctx, l) + stop, err := test.Serve(srv, test.NewHandler()) + if err != nil { + panic(err) + } + + go func() { + for { + select { + case <-ctx.Done(): + return + default: + } + if err := srv.Accept(); err != nil { + if errors.Is(err, net.ErrClosed) { + return + } + return + } + } + }() + + client := wrpctcp.NewInvoker(addr) + done := make(chan error, 1) + go func() { + done <- runner.Run(ctx, client) + }() + select { + case err := <-done: + cancel() + _ = stop() + _ = l.Close() + if err != nil { + os.Stderr.WriteString(err.Error() + "\n") + os.Exit(1) + } + case <-time.After(60 * time.Second): + panic("runtime test timed out") + } +} +"#; + +impl Runner<'_> { + /// Generates Go bindings for `component`'s world into `dir` using `pkg` as + /// the module import path, then copies the component's source file in + /// beside them so both share the generated world package. + fn generate_go_component(&self, component: &Component, dir: &Path, pkg: &str) -> Result<()> { + let mut cmd = Command::new(self.wit_bindgen); + cmd.arg("go") + .arg(&component.bindgen.wit_path) + .arg("--world") + .arg(format!("%{}", component.bindgen.world)) + .arg("--out-dir") + .arg(dir) + .arg("--generate-all") + .arg("--package") + .arg(pkg) + .arg("--gofmt=false"); + self.run_command(&mut cmd) + .context("failed to generate Go bindings")?; + + let src = std::fs::read_to_string(&component.path)?; + // Avoid a `*_test.go` filename (e.g. for a component named `test`); Go + // excludes those from non-test builds. + super::write_if_different(&dir.join(format!("{}_impl.go", component.name)), src)?; + Ok(()) + } + + /// Builds and runs a single Go runtime test by linking the `runner` and + /// `test` into one Go program connected over an in-process TCP transport. + pub(crate) fn go_runtime_test( + &self, + case: &Test, + runner: &Component, + tst: &Component, + ) -> Result<()> { + let cwd = env::current_dir()?; + let go_module = match &self.opts.go.go_wrpc_path { + Some(path) => cwd.join(path), + None => cwd.join("go"), + }; + + let driver = cwd + .join(&self.opts.artifacts) + .join(&case.name) + .join(format!("{}-{}-go", runner.name, tst.name)); + + self.generate_go_component(runner, &driver.join("runner"), "driver/runner")?; + self.generate_go_component(tst, &driver.join("test"), "driver/test")?; + + super::write_if_different( + &driver.join("go.mod"), + format!( + "module driver\n\ngo 1.25.0\n\nrequire wrpc.io/go v0.0.0-unpublished\n\nreplace wrpc.io/go v0.0.0-unpublished => {}\n", + go_module.display(), + ), + )?; + super::write_if_different(&driver.join("main.go"), DRIVER)?; + + self.run_command(Command::new("go").args(["run", "."]).current_dir(&driver)) + .context("Go runtime test driver failed") + } +} diff --git a/crates/wit-bindgen-test/src/lib.rs b/crates/wit-bindgen-test/src/lib.rs index 75c815c07..b8fe31b19 100644 --- a/crates/wit-bindgen-test/src/lib.rs +++ b/crates/wit-bindgen-test/src/lib.rs @@ -281,8 +281,11 @@ impl Runner<'_> { /// Returns a list of components that were found within this directory. fn load_test(&self, wit: &Path, dir: &Path) -> Result> { let mut resolve = wit_parser::Resolve::default(); - let pkg = resolve - .push_file(&wit) + // Use `push_path` on the test directory (not `push_file` on `test.wit`) + // so a sibling `deps/` directory of dependency packages is resolved, + // matching how codegen tests are loaded. + let (pkg, _) = resolve + .push_path(&dir) .context("failed to load `test.wit` in test directory")?; let resolve = Arc::new(resolve); @@ -340,7 +343,9 @@ impl Runner<'_> { Kind::Runner => runner_world.clone(), Kind::Test => test_world.clone(), }, - wit_path: wit.to_path_buf(), + // Point at the test directory rather than `test.wit` so the + // bindings generator resolves a sibling `deps/` directory. + wit_path: dir.to_path_buf(), }; let component = self @@ -587,29 +592,36 @@ impl Runner<'_> { let compile_results = components .par_iter() .map(|(test, component)| { + let should_fail = Self::runtime_test_should_fail(&test.name); let path = self .compile_component(test, component) .with_context(|| format!("failed to compile component {:?}", component.path)); - self.update_status(&path, false); - (test, component, path) + self.update_status(&path, should_fail); + (test, component, path, should_fail) }) .collect::>(); println!(""); let mut compilations = Vec::new(); - self.render_errors( - compile_results - .into_iter() - .map(|(test, component, result)| match result { - Ok(path) => { + self.render_errors(compile_results.into_iter().map( + |(test, component, result, should_fail)| { + match result { + // A test expected to fail that nonetheless compiled should be + // flagged so its xfail entry can be removed. + Ok(path) if !should_fail => { compilations.push((test, component, path)); StepResult::new("", Ok(())) } + Ok(_) => StepResult::new(&test.name, Ok(())) + .should_fail(true) + .metadata("component", &component.name), Err(e) => StepResult::new(&test.name, Err(e)) + .should_fail(should_fail) .metadata("component", &component.name) .metadata("path", component.path.display()), - }), - ); + } + }, + )); // Next, massage the data a bit. Create a map of all tests to where // their components are located. Then perform a product of runners/tests @@ -706,7 +718,7 @@ impl Runner<'_> { Language::Rust => { self.rust_runtime_test(case, runner, runner_bindings, test, test_bindings) } - Language::Go => bail!("Go runtime tests are not yet supported"), + Language::Go => self.go_runtime_test(case, runner, test), } } @@ -747,6 +759,16 @@ status: {}", bail!("{error}") } + /// Returns whether the named runtime test is expected to fail. + /// + /// The wRPC generators do not yet implement the `map` or fixed-length + /// `list` WIT types, so bindings generation for those tests panics. + /// Their failure is expected; if a test here starts succeeding, the harness + /// flags it so this entry can be removed. + fn runtime_test_should_fail(name: &str) -> bool { + matches!(name, "map" | "fixed-length-lists") + } + /// "poor man's test output progress" fn update_status(&self, result: &Result, should_fail: bool) { if result.is_ok() == !should_fail { diff --git a/tests/runtime/fixed-length-lists/runner.rs b/tests/runtime/fixed-length-lists/runner.rs new file mode 100644 index 000000000..3e59cef12 --- /dev/null +++ b/tests/runtime/fixed-length-lists/runner.rs @@ -0,0 +1,74 @@ +//@ wasmtime-flags = '-Wcomponent-model-fixed-length-lists' + +include!(env!("BINDINGS")); + +use test::fixed_length_lists::to_test::*; + +struct Component; + +export!(Component); + +impl Guest for Component { + fn run() { + list_param([1, 2, 3, 4]); + list_param2([[1, 2], [3, 4]]); + list_param3([ + -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15, 16, -17, 18, -19, 20, + ]); + { + let result = list_result(); + assert_eq!(result, [b'0', b'1', b'A', b'B', b'a', b'b', 128, 255]); + } + { + let result = list_minmax16([0, 1024, 32768, 65535], [1, 2048, -32767, -2]); + assert_eq!(result, ([0, 1024, 32768, 65535], [1, 2048, -32767, -2])); + } + { + let result = list_minmax_float([2.0, -42.0], [0.25, -0.125]); + assert_eq!(result, ([2.0, -42.0], [0.25, -0.125])); + } + { + let result = + list_roundtrip([b'a', b'b', b'c', b'd', 0, 1, 2, 3, b'A', b'B', b'Y', b'Z']); + assert_eq!( + result, + [b'a', b'b', b'c', b'd', 0, 1, 2, 3, b'A', b'B', b'Y', b'Z'] + ); + } + { + let result = nested_roundtrip([[1, 5], [42, 1_000_000]], [[-1, 3], [-2_000_000, 4711]]); + assert_eq!( + result, + ([[1, 5], [42, 1_000_000]], [[-1, 3], [-2_000_000, 4711]]) + ); + } + { + let result = large_roundtrip( + [[1, 5], [42, 1_000_000]], + [ + [-1, 3, -2, 4], + [-2_000_000, 4711, 99_999, -5], + [-6, 7, 8, -9], + [50, -5, 500, -5000], + ], + ); + assert_eq!( + result, + ( + [[1, 5], [42, 1_000_000]], + [ + [-1, 3, -2, 4], + [-2_000_000, 4711, 99_999, -5], + [-6, 7, 8, -9], + [50, -5, 500, -5000] + ] + ) + ); + } + { + let result = nightmare_on_cpp([Nested { l: [1, -1] }, Nested { l: [2, -2] }]); + assert_eq!(result[0].l, [1, -1]); + assert_eq!(result[1].l, [2, -2]); + } + } +} diff --git a/tests/runtime/fixed-length-lists/test.rs b/tests/runtime/fixed-length-lists/test.rs new file mode 100644 index 000000000..566ef7d6d --- /dev/null +++ b/tests/runtime/fixed-length-lists/test.rs @@ -0,0 +1,43 @@ +include!(env!("BINDINGS")); + +struct Component; + +export!(Component); + +use crate::exports::test::fixed_length_lists::to_test::Nested; + +impl exports::test::fixed_length_lists::to_test::Guest for Component { + fn list_param(a: [u32; 4]) { + assert_eq!(a, [1, 2, 3, 4]); + } + fn list_param2(a: [[u32; 2]; 2]) { + assert_eq!(a, [[1, 2], [3, 4]]); + } + fn list_param3(a: [i32; 20]) { + assert_eq!( + a, + [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15, 16, -17, 18, -19, 20] + ); + } + fn list_minmax16(a: [u16; 4], b: [i16; 4]) -> ([u16; 4], [i16; 4]) { + (a, b) + } + fn list_minmax_float(a: [f32; 2], b: [f64; 2]) -> ([f32; 2], [f64; 2]) { + (a, b) + } + fn list_roundtrip(a: [u8; 12]) -> [u8; 12] { + a + } + fn list_result() -> [u8; 8] { + [b'0', b'1', b'A', b'B', b'a', b'b', 128, 255] + } + fn nested_roundtrip(a: [[u32; 2]; 2], b: [[i32; 2]; 2]) -> ([[u32; 2]; 2], [[i32; 2]; 2]) { + (a, b) + } + fn large_roundtrip(a: [[u32; 2]; 2], b: [[i32; 4]; 4]) -> ([[u32; 2]; 2], [[i32; 4]; 4]) { + (a, b) + } + fn nightmare_on_cpp(a: [Nested; 2]) -> [Nested; 2] { + a + } +} diff --git a/tests/runtime/fixed-length-lists/test.wit b/tests/runtime/fixed-length-lists/test.wit new file mode 100644 index 000000000..892d9296f --- /dev/null +++ b/tests/runtime/fixed-length-lists/test.wit @@ -0,0 +1,33 @@ +package test:fixed-length-lists; + +interface to-test { + list-param: func(a: list); + list-param2: func(a: list, 2>); + list-param3: func(a: list); + list-result: func() -> list; + + list-minmax16: func(a: list, b: list) -> tuple, list>; + list-minmax-float: func(a: list, b: list) + -> tuple, list>; + + list-roundtrip: func(a: list) -> list; + + nested-roundtrip: func(a: list, 2>, b: list, 2>) -> tuple,2>, list, 2>>; + large-roundtrip: func(a: list, 2>, b: list, 4>) -> tuple,2>, list, 4>>; + + record nested { + l: list, + } + + nightmare-on-cpp: func(a: list) -> list; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/flavorful/runner.go b/tests/runtime/flavorful/runner.go new file mode 100644 index 000000000..460827f4a --- /dev/null +++ b/tests/runtime/flavorful/runner.go @@ -0,0 +1,91 @@ +package runner + +import ( + "context" + "fmt" + "slices" + + wrpc "wrpc.io/go" + + "driver/runner/test/flavorful/to_test" +) + +func ptr[T any](v T) *T { + return &v +} + +func Run(ctx context.Context, c wrpc.Invoker) error { + must0(to_test.FListInRecord1(ctx, c, &to_test.ListInRecord1{A: "list_in_record1"})) + + assertEqual(must(to_test.FListInRecord2(ctx, c)).A, "list_in_record2") + + assertEqual(must(to_test.FListInRecord3(ctx, c, &to_test.ListInRecord3{A: "list_in_record3 input"})).A, "list_in_record3 output") + + assertEqual(must(to_test.FListInRecord4(ctx, c, &to_test.ListInAlias{A: "input4"})).A, "result4") + + must0(to_test.FListInVariant1(ctx, c, ptr("foo"), wrpc.Err[struct{}]("bar"))) + + assertEqual(*must(to_test.FListInVariant2(ctx, c)), "list_in_variant2") + + assertEqual(*must(to_test.FListInVariant3(ctx, c, ptr("input3"))), "output3") + + res1 := must(to_test.ErrnoResult(ctx, c)) + assert(res1.Err != nil) + res2 := must(to_test.ErrnoResult(ctx, c)) + assert(res2.Ok != nil) + + { + a, b, err := to_test.ListTypedefs(ctx, c, "typedef1", []string{"typedef2"}) + if err != nil { + panic(err) + } + assert(slices.Equal(a, []byte("typedef3"))) + assertEqual(len(b), 1) + assertEqual(b[0], "typedef4") + } + + { + a, b, cc, err := to_test.ListOfVariants(ctx, c, + []bool{true, false}, + []*wrpc.Result[struct{}, struct{}]{ + wrpc.Ok[struct{}](struct{}{}), + wrpc.Err[struct{}](struct{}{}), + }, + []to_test.MyErrno{to_test.MyErrno_Success, to_test.MyErrno_A}, + ) + if err != nil { + panic(err) + } + assert(slices.Equal(a, []bool{false, true})) + assertEqual(len(b), 2) + assert(b[0].Err != nil) + assert(b[1].Ok != nil) + assert(slices.Equal(cc, []to_test.MyErrno{to_test.MyErrno_A, to_test.MyErrno_B})) + } + return nil +} + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +func must0(err error) { + if err != nil { + panic(err) + } +} + +func assertEqual[T comparable](a T, b T) { + if a != b { + panic(fmt.Sprintf("%v not equal to %v", a, b)) + } +} + +func assert(v bool) { + if !v { + panic("assertion failed") + } +} diff --git a/tests/runtime/flavorful/runner.rs b/tests/runtime/flavorful/runner.rs new file mode 100644 index 000000000..6505efd4c --- /dev/null +++ b/tests/runtime/flavorful/runner.rs @@ -0,0 +1,77 @@ +use crate::runner::test::flavorful::to_test::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + f_list_in_record1( + wrpc, + (), + &ListInRecord1 { + a: "list_in_record1".to_string(), + }, + ) + .await?; + assert_eq!(f_list_in_record2(wrpc, ()).await?.a, "list_in_record2"); + + assert_eq!( + f_list_in_record3( + wrpc, + (), + &ListInRecord3 { + a: "list_in_record3 input".to_string() + } + ) + .await? + .a, + "list_in_record3 output" + ); + + assert_eq!( + f_list_in_record4( + wrpc, + (), + &ListInAlias { + a: "input4".to_string() + } + ) + .await? + .a, + "result4" + ); + + f_list_in_variant1(wrpc, (), Some("foo"), &Err("bar".to_string())).await?; + assert_eq!( + f_list_in_variant2(wrpc, ()).await?, + Some("list_in_variant2".to_string()) + ); + assert_eq!( + f_list_in_variant3(wrpc, (), Some("input3")).await?, + Some("output3".to_string()) + ); + + assert!(errno_result(wrpc, ()).await?.is_err()); + MyErrno::A.to_string(); + _ = format!("{:?}", MyErrno::A); + fn assert_error() {} + assert_error::(); + + assert!(errno_result(wrpc, ()).await?.is_ok()); + + let (a, b) = list_typedefs(wrpc, (), "typedef1", &["typedef2"]).await?; + assert_eq!(a.as_ref(), b"typedef3"); + assert_eq!(b.len(), 1); + assert_eq!(b[0], "typedef4"); + + let (a, b, c) = list_of_variants( + wrpc, + (), + &[true, false], + &[Ok(()), Err(())], + &[MyErrno::Success, MyErrno::A], + ) + .await?; + assert_eq!(a, [false, true]); + assert_eq!(b, [Err(()), Ok(())]); + assert_eq!(c, [MyErrno::A, MyErrno::B]); + Ok(()) +} diff --git a/tests/runtime/flavorful/test.go b/tests/runtime/flavorful/test.go new file mode 100644 index 000000000..aed852f0b --- /dev/null +++ b/tests/runtime/flavorful/test.go @@ -0,0 +1,108 @@ +package test + +import ( + "context" + "fmt" + "slices" + "sync/atomic" + + wrpc "wrpc.io/go" + + to_test "driver/test/exports/test/flavorful/to_test" +) + +func ptr[T any](v T) *T { + return &v +} + +type handler struct { + first atomic.Bool +} + +func NewHandler() *handler { + h := &handler{} + h.first.Store(true) + return h +} + +func (*handler) FListInRecord1(ctx context.Context, a *to_test.ListInRecord1) error { + if a.A != "list_in_record1" { + return fmt.Errorf("unexpected: %q", a.A) + } + return nil +} + +func (*handler) FListInRecord2(ctx context.Context) (*to_test.ListInRecord2, error) { + return &to_test.ListInRecord2{A: "list_in_record2"}, nil +} + +func (*handler) FListInRecord3(ctx context.Context, a *to_test.ListInRecord3) (*to_test.ListInRecord3, error) { + if a.A != "list_in_record3 input" { + return nil, fmt.Errorf("unexpected: %q", a.A) + } + return &to_test.ListInRecord3{A: "list_in_record3 output"}, nil +} + +func (*handler) FListInRecord4(ctx context.Context, a *to_test.ListInRecord4) (*to_test.ListInRecord4, error) { + if a.A != "input4" { + return nil, fmt.Errorf("unexpected: %q", a.A) + } + return &to_test.ListInRecord4{A: "result4"}, nil +} + +func (*handler) FListInVariant1(ctx context.Context, a *string, b *to_test.ListInVariant1V2) error { + if a == nil || *a != "foo" { + return fmt.Errorf("unexpected a: %v", a) + } + if b.Err == nil || *b.Err != "bar" { + return fmt.Errorf("unexpected b: %v", b) + } + return nil +} + +func (*handler) FListInVariant2(ctx context.Context) (*string, error) { + return ptr("list_in_variant2"), nil +} + +func (*handler) FListInVariant3(ctx context.Context, a *string) (*string, error) { + if a == nil || *a != "input3" { + return nil, fmt.Errorf("unexpected: %v", a) + } + return ptr("output3"), nil +} + +func (h *handler) ErrnoResult(ctx context.Context) (*wrpc.Result[struct{}, to_test.MyErrno], error) { + if h.first.Swap(false) { + return wrpc.Err[struct{}](to_test.MyErrno_B), nil + } + return wrpc.Ok[to_test.MyErrno](struct{}{}), nil +} + +func (*handler) ListTypedefs(ctx context.Context, a string, c []string) ([]uint8, []string, error) { + if a != "typedef1" { + return nil, nil, fmt.Errorf("unexpected a: %q", a) + } + if !slices.Equal(c, []string{"typedef2"}) { + return nil, nil, fmt.Errorf("unexpected c: %v", c) + } + return []uint8("typedef3"), []string{"typedef4"}, nil +} + +func (*handler) ListOfVariants(ctx context.Context, bools []bool, results []*wrpc.Result[struct{}, struct{}], enums []to_test.MyErrno) ([]bool, []*wrpc.Result[struct{}, struct{}], []to_test.MyErrno, error) { + if !slices.Equal(bools, []bool{true, false}) { + return nil, nil, nil, fmt.Errorf("unexpected bools: %v", bools) + } + if len(results) != 2 || results[0].Ok == nil || results[1].Err == nil { + return nil, nil, nil, fmt.Errorf("unexpected results: %v", results) + } + if !slices.Equal(enums, []to_test.MyErrno{to_test.MyErrno_Success, to_test.MyErrno_A}) { + return nil, nil, nil, fmt.Errorf("unexpected enums: %v", enums) + } + return []bool{false, true}, + []*wrpc.Result[struct{}, struct{}]{ + wrpc.Err[struct{}](struct{}{}), + wrpc.Ok[struct{}](struct{}{}), + }, + []to_test.MyErrno{to_test.MyErrno_A, to_test.MyErrno_B}, + nil +} diff --git a/tests/runtime/flavorful/test.rs b/tests/runtime/flavorful/test.rs new file mode 100644 index 000000000..37f8d2d54 --- /dev/null +++ b/tests/runtime/flavorful/test.rs @@ -0,0 +1,126 @@ +use crate::test::exports::test::flavorful::to_test::*; + +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::test::flavorful::to_test::Handler for Component { + async fn f_list_in_record1( + &self, + _cx: Ctx, + ty: ListInRecord1, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(ty.a, "list_in_record1"); + Ok(()) + } + + async fn f_list_in_record2( + &self, + _cx: Ctx, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(ListInRecord2 { + a: "list_in_record2".to_string(), + }) + } + + async fn f_list_in_record3( + &self, + _cx: Ctx, + a: ListInRecord3, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + assert_eq!(a.a, "list_in_record3 input"); + Ok(ListInRecord3 { + a: "list_in_record3 output".to_string(), + }) + } + + async fn f_list_in_record4( + &self, + _cx: Ctx, + a: ListInAlias, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + assert_eq!(a.a, "input4"); + Ok(ListInRecord4 { + a: "result4".to_string(), + }) + } + + async fn f_list_in_variant1( + &self, + _cx: Ctx, + a: ListInVariant1V1, + b: ListInVariant1V2, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(a.unwrap(), "foo"); + assert_eq!(b.unwrap_err(), "bar"); + Ok(()) + } + + async fn f_list_in_variant2( + &self, + _cx: Ctx, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(Some("list_in_variant2".to_string())) + } + + async fn f_list_in_variant3( + &self, + _cx: Ctx, + a: ListInVariant3, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + assert_eq!(a.unwrap(), "input3"); + Ok(Some("output3".to_string())) + } + + async fn errno_result( + &self, + _cx: Ctx, + ) -> ::wit_bindgen_wrpc::anyhow::Result<::core::result::Result<(), MyErrno>> { + static FIRST: ::std::sync::atomic::AtomicBool = ::std::sync::atomic::AtomicBool::new(true); + MyErrno::A.to_string(); + _ = format!("{:?}", MyErrno::A); + fn assert_error() {} + assert_error::(); + + if FIRST.swap(false, ::std::sync::atomic::Ordering::SeqCst) { + Ok(Err(MyErrno::B)) + } else { + Ok(Ok(())) + } + } + + async fn list_typedefs( + &self, + _cx: Ctx, + a: ListTypedef, + c: ListTypedef3, + ) -> ::wit_bindgen_wrpc::anyhow::Result<(ListTypedef2, ListTypedef3)> { + assert_eq!(a, "typedef1"); + assert_eq!(c.len(), 1); + assert_eq!(c[0], "typedef2"); + Ok(( + ::wit_bindgen_wrpc::bytes::Bytes::from_static(b"typedef3"), + vec!["typedef4".to_string()], + )) + } + + async fn list_of_variants( + &self, + _cx: Ctx, + bools: Vec, + results: Vec<::core::result::Result<(), ()>>, + enums: Vec, + ) -> ::wit_bindgen_wrpc::anyhow::Result<( + Vec, + Vec<::core::result::Result<(), ()>>, + Vec, + )> { + assert_eq!(bools, [true, false]); + assert_eq!(results, [Ok(()), Err(())]); + assert_eq!(enums, [MyErrno::Success, MyErrno::A]); + Ok(( + vec![false, true], + vec![Err(()), Ok(())], + vec![MyErrno::A, MyErrno::B], + )) + } +} diff --git a/tests/runtime/flavorful/test.wit b/tests/runtime/flavorful/test.wit new file mode 100644 index 000000000..de6a85396 --- /dev/null +++ b/tests/runtime/flavorful/test.wit @@ -0,0 +1,45 @@ +package test:flavorful; + +interface to-test { + record list-in-record1 { a: string } + record list-in-record2 { a: string } + record list-in-record3 { a: string } + record list-in-record4 { a: string } + type list-in-alias = list-in-record4; + + f-list-in-record1: func(a: list-in-record1); + f-list-in-record2: func() -> list-in-record2; + f-list-in-record3: func(a: list-in-record3) -> list-in-record3; + f-list-in-record4: func(a: list-in-alias) -> list-in-alias; + + type list-in-variant1-v1 = option; + type list-in-variant1-v2 = result<_, string>; + f-list-in-variant1: func(a: list-in-variant1-v1, b: list-in-variant1-v2); + + type list-in-variant2 = option; + f-list-in-variant2: func() -> list-in-variant2; + + type list-in-variant3 = option; + f-list-in-variant3: func(a: list-in-variant3) -> list-in-variant3; + + enum my-errno { success, a, b } + errno-result: func() -> result<_, my-errno>; + + type list-typedef = string; + type list-typedef2 = list; + type list-typedef3 = list; + list-typedefs: func(a: list-typedef, c: list-typedef3) -> tuple; + + list-of-variants: func(a: list, b: list, c: list) + -> tuple, list, list>; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/gated-features/runner.rs b/tests/runtime/gated-features/runner.rs new file mode 100644 index 000000000..ee5a07e52 --- /dev/null +++ b/tests/runtime/gated-features/runner.rs @@ -0,0 +1,9 @@ +//@ args = '--features y' + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + crate::runner::foo::bar::bindings::y(wrpc, ()).await?; + crate::runner::foo::bar::bindings::z(wrpc, ()).await?; + Ok(()) +} diff --git a/tests/runtime/gated-features/test.rs b/tests/runtime/gated-features/test.rs new file mode 100644 index 000000000..ac6214831 --- /dev/null +++ b/tests/runtime/gated-features/test.rs @@ -0,0 +1,14 @@ +//@ args = '--features y' + +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::foo::bar::bindings::Handler for Component { + async fn y(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + Ok(()) + } + + async fn z(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + Ok(()) + } +} diff --git a/tests/runtime/gated-features/test.wit b/tests/runtime/gated-features/test.wit new file mode 100644 index 000000000..27b83dcde --- /dev/null +++ b/tests/runtime/gated-features/test.wit @@ -0,0 +1,19 @@ +package foo:bar@1.2.3; + +interface bindings { + @unstable(feature = x) + x: func(); + @unstable(feature = y) + y: func(); + @since(version = 1.2.3) + z: func(); +} + +world test { + export bindings; +} +world runner { + import bindings; + + export run: func(); +} diff --git a/tests/runtime/list-in-variant/runner.rs b/tests/runtime/list-in-variant/runner.rs new file mode 100644 index 000000000..302f4951e --- /dev/null +++ b/tests/runtime/list-in-variant/runner.rs @@ -0,0 +1,36 @@ +use crate::runner::test::list_in_variant::to_test::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let hw: Vec<&str> = vec!["hello", "world"]; + assert_eq!( + list_in_option(wrpc, (), Some(hw.as_slice())).await?, + "hello,world" + ); + assert_eq!(list_in_option(wrpc, (), None).await?, "none"); + + let fbb = PayloadOrEmpty::WithData(vec!["foo".into(), "bar".into(), "baz".into()]); + assert_eq!(list_in_variant(wrpc, (), &fbb).await?, "foo,bar,baz"); + assert_eq!( + list_in_variant(wrpc, (), &PayloadOrEmpty::Empty).await?, + "empty" + ); + + let abc: Result, String> = Ok(vec!["a".into(), "b".into(), "c".into()]); + assert_eq!(list_in_result(wrpc, (), &abc).await?, "a,b,c"); + let oops: Result, String> = Err("oops".to_string()); + assert_eq!(list_in_result(wrpc, (), &oops).await?, "err:oops"); + + let hw2: Vec<&str> = vec!["hello", "world"]; + let s = list_in_option_with_return(wrpc, (), Some(hw2.as_slice())).await?; + assert_eq!(s.count, 2); + assert_eq!(s.label, "hello,world"); + let s = list_in_option_with_return(wrpc, (), None).await?; + assert_eq!(s.count, 0); + assert_eq!(s.label, "none"); + + let xyz: Vec<&str> = vec!["x", "y", "z"]; + assert_eq!(top_level_list(wrpc, (), &xyz).await?, "x,y,z"); + Ok(()) +} diff --git a/tests/runtime/list-in-variant/test.rs b/tests/runtime/list-in-variant/test.rs new file mode 100644 index 000000000..1cf667559 --- /dev/null +++ b/tests/runtime/list-in-variant/test.rs @@ -0,0 +1,64 @@ +use crate::test::exports::test::list_in_variant::to_test::*; + +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::test::list_in_variant::to_test::Handler for Component { + async fn list_in_option( + &self, + _cx: Ctx, + data: Option>, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(match data { + Some(list) => list.join(","), + None => "none".to_string(), + }) + } + + async fn list_in_variant( + &self, + _cx: Ctx, + data: PayloadOrEmpty, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(match data { + PayloadOrEmpty::WithData(list) => list.join(","), + PayloadOrEmpty::Empty => "empty".to_string(), + }) + } + + async fn list_in_result( + &self, + _cx: Ctx, + data: Result, String>, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(match data { + Ok(list) => list.join(","), + Err(e) => format!("err:{e}"), + }) + } + + async fn list_in_option_with_return( + &self, + _cx: Ctx, + data: Option>, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(match data { + Some(list) => Summary { + count: list.len() as u32, + label: list.join(","), + }, + None => Summary { + count: 0, + label: "none".to_string(), + }, + }) + } + + async fn top_level_list( + &self, + _cx: Ctx, + items: Vec, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(items.join(",")) + } +} diff --git a/tests/runtime/list-in-variant/test.wit b/tests/runtime/list-in-variant/test.wit new file mode 100644 index 000000000..da156e5d1 --- /dev/null +++ b/tests/runtime/list-in-variant/test.wit @@ -0,0 +1,31 @@ +package test:list-in-variant; + +interface to-test { + list-in-option: func(data: option>) -> string; + + variant payload-or-empty { + empty, + with-data(list), + } + list-in-variant: func(data: payload-or-empty) -> string; + + list-in-result: func(data: result, string>) -> string; + + record summary { + count: u32, + label: string, + } + list-in-option-with-return: func(data: option>) -> summary; + + top-level-list: func(items: list) -> string; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/lists-alias/runner.rs b/tests/runtime/lists-alias/runner.rs new file mode 100644 index 000000000..30a16df39 --- /dev/null +++ b/tests/runtime/lists-alias/runner.rs @@ -0,0 +1,13 @@ +use ::wit_bindgen_wrpc::bytes::Bytes; + +use crate::runner::cat::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + foo(wrpc, (), &Bytes::from_static(b"hello")).await?; + + let t: Bytes = bar(wrpc, ()).await?; + assert_eq!(t, &b"world"[..]); + Ok(()) +} diff --git a/tests/runtime/lists-alias/test.rs b/tests/runtime/lists-alias/test.rs new file mode 100644 index 000000000..2827e242a --- /dev/null +++ b/tests/runtime/lists-alias/test.rs @@ -0,0 +1,15 @@ +use ::wit_bindgen_wrpc::bytes::Bytes; + +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::cat::Handler for Component { + async fn foo(&self, _cx: Ctx, x: Bytes) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(x, &b"hello"[..]); + Ok(()) + } + + async fn bar(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(Bytes::from_static(b"world")) + } +} diff --git a/tests/runtime/lists-alias/test.wit b/tests/runtime/lists-alias/test.wit new file mode 100644 index 000000000..ea0c15c1d --- /dev/null +++ b/tests/runtime/lists-alias/test.wit @@ -0,0 +1,19 @@ +package my:lists; + +world runner { + import cat: interface { + type my-list = list; + foo: func(x: my-list); + bar: func() -> my-list; + } + + export run: func(); +} + +world test { + export cat: interface { + type my-list = list; + foo: func(x: my-list); + bar: func() -> my-list; + } +} diff --git a/tests/runtime/lists/alloc.rs b/tests/runtime/lists/alloc.rs new file mode 100644 index 000000000..0bb2559da --- /dev/null +++ b/tests/runtime/lists/alloc.rs @@ -0,0 +1,30 @@ +use std::alloc::{GlobalAlloc, Layout, System}; +use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + +#[global_allocator] +static ALLOC: A = A; + +static ALLOC_AMT: AtomicUsize = AtomicUsize::new(0); + +struct A; + +unsafe impl GlobalAlloc for A { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let ptr = System.alloc(layout); + if !ptr.is_null() { + ALLOC_AMT.fetch_add(layout.size(), SeqCst); + } + return ptr; + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // Poison all deallocations to try to catch any use-after-free in the + // bindings as early as possible. + std::ptr::write_bytes(ptr, 0xde, layout.size()); + ALLOC_AMT.fetch_sub(layout.size(), SeqCst); + System.dealloc(ptr, layout) + } +} +pub fn get() -> usize { + ALLOC_AMT.load(SeqCst) +} diff --git a/tests/runtime/lists/runner.go b/tests/runtime/lists/runner.go new file mode 100644 index 000000000..846faebd1 --- /dev/null +++ b/tests/runtime/lists/runner.go @@ -0,0 +1,76 @@ +package runner + +import ( + "context" + "fmt" + "slices" + + wrpc "wrpc.io/go" + + "driver/runner/test/lists/to_test" +) + +func Run(ctx context.Context, c wrpc.Invoker) error { + must0(to_test.EmptyListParam(ctx, c, []uint8{})) + must0(to_test.EmptyStringParam(ctx, c, "")) + assertEqual(0, len(must(to_test.EmptyListResult(ctx, c)))) + assertEqual(0, len(must(to_test.EmptyStringResult(ctx, c)))) + must0(to_test.ListParam(ctx, c, []uint8{1, 2, 3, 4})) + must0(to_test.ListParam2(ctx, c, "foo")) + must0(to_test.ListParam3(ctx, c, []string{"foo", "bar", "baz"})) + must0(to_test.ListParam4(ctx, c, [][]string{{"foo", "bar"}, {"baz"}})) + must0(to_test.ListParam5(ctx, c, []*wrpc.Tuple3[uint8, uint32, uint8]{ + {V0: 1, V1: 2, V2: 3}, + {V0: 4, V1: 5, V2: 6}, + })) + + large := make([]string, 0, 1000) + for i := 0; i < 1000; i++ { + large = append(large, "string") + } + must0(to_test.ListParamLarge(ctx, c, large)) + + assert(slices.Equal(must(to_test.ListResult(ctx, c)), []uint8{1, 2, 3, 4, 5})) + assertEqual(must(to_test.ListResult2(ctx, c)), "hello!") + assert(slices.Equal(must(to_test.ListResult3(ctx, c)), []string{"hello,", "world!"})) + assert(slices.Equal(must(to_test.ListRoundtrip(ctx, c, []uint8{})), []uint8{})) + + { + headers := []*wrpc.Tuple2[string, []uint8]{ + {V0: "Content-Type", V1: []uint8("text/plain")}, + {V0: "Content-Length", V1: []uint8("9")}, + } + result := must(to_test.WasiHttpHeadersRoundtrip(ctx, c, headers)) + assertEqual(len(result), 2) + assertEqual(result[0].V0, "Content-Type") + assert(slices.Equal(result[0].V1, []uint8("text/plain"))) + assertEqual(result[1].V0, "Content-Length") + assert(slices.Equal(result[1].V1, []uint8("9"))) + } + return nil +} + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +func must0(err error) { + if err != nil { + panic(err) + } +} + +func assertEqual[T comparable](a T, b T) { + if a != b { + panic(fmt.Sprintf("%v not equal to %v", a, b)) + } +} + +func assert(v bool) { + if !v { + panic("assertion failed") + } +} diff --git a/tests/runtime/lists/runner.rs b/tests/runtime/lists/runner.rs new file mode 100644 index 000000000..aaec3701c --- /dev/null +++ b/tests/runtime/lists/runner.rs @@ -0,0 +1,99 @@ +use ::wit_bindgen_wrpc::bytes::Bytes; + +use crate::runner::test::lists::to_test::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + empty_list_param(wrpc, (), &Bytes::new()).await?; + empty_string_param(wrpc, (), "").await?; + assert!(empty_list_result(wrpc, ()).await?.is_empty()); + assert!(empty_string_result(wrpc, ()).await?.is_empty()); + + list_param(wrpc, (), &Bytes::from_static(&[1, 2, 3, 4])).await?; + list_param2(wrpc, (), "foo").await?; + list_param3(wrpc, (), &["foo", "bar", "baz"]).await?; + list_param4(wrpc, (), &[&["foo", "bar"][..], &["baz"][..]]).await?; + list_param5(wrpc, (), &[(1, 2, 3), (4, 5, 6)]).await?; + let large_list: Vec = (0..1000).map(|_| "string".to_string()).collect(); + let large_list_refs: Vec<&str> = large_list.iter().map(String::as_str).collect(); + list_param_large(wrpc, (), &large_list_refs).await?; + assert_eq!(list_result(wrpc, ()).await?, &[1, 2, 3, 4, 5][..]); + assert_eq!(list_result2(wrpc, ()).await?, "hello!"); + assert_eq!(list_result3(wrpc, ()).await?, ["hello,", "world!"]); + + assert_eq!(list_roundtrip(wrpc, (), &Bytes::new()).await?, &[][..]); + assert_eq!( + list_roundtrip(wrpc, (), &Bytes::from_static(b"x")).await?, + &b"x"[..] + ); + assert_eq!( + list_roundtrip(wrpc, (), &Bytes::from_static(b"hello")).await?, + &b"hello"[..] + ); + + assert_eq!(string_roundtrip(wrpc, (), "x").await?, "x"); + assert_eq!(string_roundtrip(wrpc, (), "").await?, ""); + assert_eq!(string_roundtrip(wrpc, (), "hello").await?, "hello"); + assert_eq!( + string_roundtrip(wrpc, (), "hello ⚑ world").await?, + "hello ⚑ world" + ); + + assert_eq!( + list_minmax8( + wrpc, + (), + &Bytes::from_static(&[u8::MIN, u8::MAX]), + &[i8::MIN, i8::MAX] + ) + .await?, + ( + Bytes::from_static(&[u8::MIN, u8::MAX]), + vec![i8::MIN, i8::MAX] + ), + ); + assert_eq!( + list_minmax16(wrpc, (), &[u16::MIN, u16::MAX], &[i16::MIN, i16::MAX]).await?, + (vec![u16::MIN, u16::MAX], vec![i16::MIN, i16::MAX]), + ); + assert_eq!( + list_minmax32(wrpc, (), &[u32::MIN, u32::MAX], &[i32::MIN, i32::MAX]).await?, + (vec![u32::MIN, u32::MAX], vec![i32::MIN, i32::MAX]), + ); + assert_eq!( + list_minmax64(wrpc, (), &[u64::MIN, u64::MAX], &[i64::MIN, i64::MAX]).await?, + (vec![u64::MIN, u64::MAX], vec![i64::MIN, i64::MAX]), + ); + assert_eq!( + list_minmax_float( + wrpc, + (), + &[f32::MIN, f32::MAX, f32::NEG_INFINITY, f32::INFINITY], + &[f64::MIN, f64::MAX, f64::NEG_INFINITY, f64::INFINITY] + ) + .await?, + ( + vec![f32::MIN, f32::MAX, f32::NEG_INFINITY, f32::INFINITY], + vec![f64::MIN, f64::MAX, f64::NEG_INFINITY, f64::INFINITY], + ), + ); + + let headers = [ + ("Content-Type", Bytes::from_static(b"text/plain")), + ( + "Content-Length", + Bytes::from("Not found".len().to_string().into_bytes()), + ), + ]; + let headers_refs: Vec<(&str, &Bytes)> = headers.iter().map(|(k, v)| (*k, v)).collect(); + let result = wasi_http_headers_roundtrip(wrpc, (), &headers_refs).await?; + assert_eq!(result[0].0, "Content-Type"); + assert_eq!(result[0].1, &b"text/plain"[..]); + assert_eq!(result[1].0, "Content-Length"); + assert_eq!( + result[1].1, + Bytes::from("Not found".len().to_string().into_bytes()) + ); + Ok(()) +} diff --git a/tests/runtime/lists/test.go b/tests/runtime/lists/test.go new file mode 100644 index 000000000..40b2a2a6f --- /dev/null +++ b/tests/runtime/lists/test.go @@ -0,0 +1,138 @@ +package test + +import ( + "context" + "slices" + + wrpc "wrpc.io/go" +) + +type handler struct{} + +func NewHandler() handler { + return handler{} +} + +func (handler) AllocatedBytes(ctx context.Context) (uint32, error) { + return 0, nil +} + +func (handler) EmptyListParam(ctx context.Context, x []uint8) error { + if len(x) != 0 { + panic("trouble") + } + return nil +} + +func (handler) EmptyStringParam(ctx context.Context, x string) error { + if len(x) != 0 { + panic("trouble") + } + return nil +} + +func (handler) EmptyListResult(ctx context.Context) ([]uint8, error) { + return []uint8{}, nil +} + +func (handler) EmptyStringResult(ctx context.Context) (string, error) { + return "", nil +} + +func (handler) ListParam(ctx context.Context, x []uint8) error { + if !slices.Equal(x, []uint8{1, 2, 3, 4}) { + panic("trouble") + } + return nil +} + +func (handler) ListParam2(ctx context.Context, x string) error { + if x != "foo" { + panic("trouble") + } + return nil +} + +func (handler) ListParam3(ctx context.Context, x []string) error { + if !slices.Equal(x, []string{"foo", "bar", "baz"}) { + panic("trouble") + } + return nil +} + +func (handler) ListParam4(ctx context.Context, x [][]string) error { + if !slices.Equal(x[0], []string{"foo", "bar"}) { + panic("trouble") + } + if !slices.Equal(x[1], []string{"baz"}) { + panic("trouble") + } + return nil +} + +func (handler) ListParam5(ctx context.Context, x []*wrpc.Tuple3[uint8, uint32, uint8]) error { + expected := []wrpc.Tuple3[uint8, uint32, uint8]{ + {V0: 1, V1: 2, V2: 3}, + {V0: 4, V1: 5, V2: 6}, + } + if len(x) != len(expected) { + panic("trouble") + } + for i, v := range x { + if *v != expected[i] { + panic("trouble") + } + } + return nil +} + +func (handler) ListParamLarge(ctx context.Context, x []string) error { + if len(x) != 1000 { + panic("trouble") + } + return nil +} + +func (handler) ListResult(ctx context.Context) ([]uint8, error) { + return []uint8{1, 2, 3, 4, 5}, nil +} + +func (handler) ListResult2(ctx context.Context) (string, error) { + return "hello!", nil +} + +func (handler) ListResult3(ctx context.Context) ([]string, error) { + return []string{"hello,", "world!"}, nil +} + +func (handler) ListRoundtrip(ctx context.Context, x []uint8) ([]uint8, error) { + return x, nil +} + +func (handler) StringRoundtrip(ctx context.Context, x string) (string, error) { + return x, nil +} + +func (handler) ListMinmax8(ctx context.Context, x []uint8, y []int8) ([]uint8, []int8, error) { + return x, y, nil +} + +func (handler) ListMinmax16(ctx context.Context, x []uint16, y []int16) ([]uint16, []int16, error) { + return x, y, nil +} + +func (handler) ListMinmax32(ctx context.Context, x []uint32, y []int32) ([]uint32, []int32, error) { + return x, y, nil +} + +func (handler) ListMinmax64(ctx context.Context, x []uint64, y []int64) ([]uint64, []int64, error) { + return x, y, nil +} + +func (handler) ListMinmaxFloat(ctx context.Context, x []float32, y []float64) ([]float32, []float64, error) { + return x, y, nil +} + +func (handler) WasiHttpHeadersRoundtrip(ctx context.Context, x []*wrpc.Tuple2[string, []uint8]) ([]*wrpc.Tuple2[string, []uint8], error) { + return x, nil +} diff --git a/tests/runtime/lists/test.rs b/tests/runtime/lists/test.rs new file mode 100644 index 000000000..df45f2211 --- /dev/null +++ b/tests/runtime/lists/test.rs @@ -0,0 +1,161 @@ +use ::wit_bindgen_wrpc::bytes::Bytes; + +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::test::lists::to_test::Handler for Component { + async fn allocated_bytes(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(0) + } + + async fn empty_list_param(&self, _cx: Ctx, a: Bytes) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert!(a.is_empty()); + Ok(()) + } + + async fn empty_string_param( + &self, + _cx: Ctx, + a: String, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert!(a.is_empty()); + Ok(()) + } + + async fn empty_list_result(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(Bytes::new()) + } + + async fn empty_string_result(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(String::new()) + } + + async fn list_param(&self, _cx: Ctx, list: Bytes) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(list, &[1, 2, 3, 4][..]); + Ok(()) + } + + async fn list_param2(&self, _cx: Ctx, ptr: String) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(ptr, "foo"); + Ok(()) + } + + async fn list_param3( + &self, + _cx: Ctx, + ptr: Vec, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(ptr.len(), 3); + assert_eq!(ptr[0], "foo"); + assert_eq!(ptr[1], "bar"); + assert_eq!(ptr[2], "baz"); + Ok(()) + } + + async fn list_param4( + &self, + _cx: Ctx, + ptr: Vec>, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(ptr.len(), 2); + assert_eq!(ptr[0][0], "foo"); + assert_eq!(ptr[0][1], "bar"); + assert_eq!(ptr[1][0], "baz"); + Ok(()) + } + + async fn list_param5( + &self, + _cx: Ctx, + ptr: Vec<(u8, u32, u8)>, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(ptr, [(1, 2, 3), (4, 5, 6)]); + Ok(()) + } + + async fn list_param_large( + &self, + _cx: Ctx, + ptr: Vec, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(ptr.len(), 1000); + Ok(()) + } + + async fn list_result(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(Bytes::from_static(&[1, 2, 3, 4, 5])) + } + + async fn list_result2(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok("hello!".to_string()) + } + + async fn list_result3(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result> { + Ok(vec!["hello,".to_string(), "world!".to_string()]) + } + + async fn list_roundtrip(&self, _cx: Ctx, x: Bytes) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(x.clone()) + } + + async fn string_roundtrip( + &self, + _cx: Ctx, + x: String, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(x.clone()) + } + + async fn list_minmax8( + &self, + _cx: Ctx, + a: Bytes, + b: Vec, + ) -> ::wit_bindgen_wrpc::anyhow::Result<(Bytes, Vec)> { + Ok((a, b)) + } + + async fn list_minmax16( + &self, + _cx: Ctx, + a: Vec, + b: Vec, + ) -> ::wit_bindgen_wrpc::anyhow::Result<(Vec, Vec)> { + Ok((a, b)) + } + + async fn list_minmax32( + &self, + _cx: Ctx, + a: Vec, + b: Vec, + ) -> ::wit_bindgen_wrpc::anyhow::Result<(Vec, Vec)> { + Ok((a, b)) + } + + async fn list_minmax64( + &self, + _cx: Ctx, + a: Vec, + b: Vec, + ) -> ::wit_bindgen_wrpc::anyhow::Result<(Vec, Vec)> { + Ok((a, b)) + } + + async fn list_minmax_float( + &self, + _cx: Ctx, + a: Vec, + b: Vec, + ) -> ::wit_bindgen_wrpc::anyhow::Result<(Vec, Vec)> { + Ok((a, b)) + } + + async fn wasi_http_headers_roundtrip( + &self, + _cx: Ctx, + headers: Vec<(String, Bytes)>, + ) -> ::wit_bindgen_wrpc::anyhow::Result> { + Ok(headers) + } +} diff --git a/tests/runtime/lists/test.wit b/tests/runtime/lists/test.wit new file mode 100644 index 000000000..5b8179175 --- /dev/null +++ b/tests/runtime/lists/test.wit @@ -0,0 +1,43 @@ +package test:lists; + +interface to-test { + empty-list-param: func(a: list); + empty-string-param: func(a: string); + empty-list-result: func() -> list; + empty-string-result: func() -> string; + + list-param: func(a: list); + list-param2: func(a: string); + list-param3: func(a: list); + list-param4: func(a: list>); + list-param5: func(a: list>); + list-param-large: func(a: list); + list-result: func() -> list; + list-result2: func() -> string; + list-result3: func() -> list; + + list-minmax8: func(a: list, b: list) -> tuple, list>; + list-minmax16: func(a: list, b: list) -> tuple, list>; + list-minmax32: func(a: list, b: list) -> tuple, list>; + list-minmax64: func(a: list, b: list) -> tuple, list>; + list-minmax-float: func(a: list, b: list) + -> tuple, list>; + + list-roundtrip: func(a: list) -> list; + + string-roundtrip: func(a: string) -> string; + + wasi-http-headers-roundtrip: func(a: list>>) -> list>>; + + allocated-bytes: func() -> u32; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/many-arguments/runner.go b/tests/runtime/many-arguments/runner.go new file mode 100644 index 000000000..eeee1ff07 --- /dev/null +++ b/tests/runtime/many-arguments/runner.go @@ -0,0 +1,13 @@ +package runner + +import ( + "context" + + wrpc "wrpc.io/go" + + "driver/runner/test/many_arguments/to_test" +) + +func Run(ctx context.Context, c wrpc.Invoker) error { + return to_test.ManyArguments(ctx, c, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) +} diff --git a/tests/runtime/many-arguments/runner.rs b/tests/runtime/many-arguments/runner.rs new file mode 100644 index 000000000..e79bae3b8 --- /dev/null +++ b/tests/runtime/many-arguments/runner.rs @@ -0,0 +1,11 @@ +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + crate::runner::test::many_arguments::to_test::many_arguments( + wrpc, + (), + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + ) + .await?; + Ok(()) +} diff --git a/tests/runtime/many-arguments/test.go b/tests/runtime/many-arguments/test.go new file mode 100644 index 000000000..cb9dcb589 --- /dev/null +++ b/tests/runtime/many-arguments/test.go @@ -0,0 +1,56 @@ +package test + +import ( + "context" + "fmt" +) + +type handler struct{} + +func NewHandler() handler { + return handler{} +} + +func (handler) ManyArguments( + ctx context.Context, + a1 uint64, + a2 uint64, + a3 uint64, + a4 uint64, + a5 uint64, + a6 uint64, + a7 uint64, + a8 uint64, + a9 uint64, + a10 uint64, + a11 uint64, + a12 uint64, + a13 uint64, + a14 uint64, + a15 uint64, + a16 uint64, +) error { + assertEqual(a1, 1) + assertEqual(a2, 2) + assertEqual(a3, 3) + assertEqual(a4, 4) + assertEqual(a5, 5) + assertEqual(a6, 6) + assertEqual(a7, 7) + assertEqual(a8, 8) + assertEqual(a9, 9) + assertEqual(a10, 10) + assertEqual(a11, 11) + assertEqual(a12, 12) + assertEqual(a13, 13) + assertEqual(a14, 14) + assertEqual(a15, 15) + assertEqual(a16, 16) + return nil +} + +func assertEqual[T comparable](a T, b T) { + if a != b { + panic(fmt.Sprintf("%v not equal to %v", a, b)) + } +} diff --git a/tests/runtime/many-arguments/test.rs b/tests/runtime/many-arguments/test.rs new file mode 100644 index 000000000..34bd62672 --- /dev/null +++ b/tests/runtime/many-arguments/test.rs @@ -0,0 +1,43 @@ +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::test::many_arguments::to_test::Handler for Component { + async fn many_arguments( + &self, + _cx: Ctx, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(a1, 1); + assert_eq!(a2, 2); + assert_eq!(a3, 3); + assert_eq!(a4, 4); + assert_eq!(a5, 5); + assert_eq!(a6, 6); + assert_eq!(a7, 7); + assert_eq!(a8, 8); + assert_eq!(a9, 9); + assert_eq!(a10, 10); + assert_eq!(a11, 11); + assert_eq!(a12, 12); + assert_eq!(a13, 13); + assert_eq!(a14, 14); + assert_eq!(a15, 15); + assert_eq!(a16, 16); + Ok(()) + } +} diff --git a/tests/runtime/many-arguments/test.wit b/tests/runtime/many-arguments/test.wit new file mode 100644 index 000000000..5c38a9706 --- /dev/null +++ b/tests/runtime/many-arguments/test.wit @@ -0,0 +1,31 @@ +package test:many-arguments; + +interface to-test { + many-arguments: func( + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + ); +} + +world test { + export to-test; +} +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/map/runner.go b/tests/runtime/map/runner.go new file mode 100644 index 000000000..b4d5055cf --- /dev/null +++ b/tests/runtime/map/runner.go @@ -0,0 +1,188 @@ +//@ wasmtime-flags = '-Wcomponent-model-map' + +package export_wit_world + +import ( + "fmt" + test "wit_component/test_maps_to_test" + + . "go.bytecodealliance.org/pkg/wit/types" +) + +func Run() { + testNamedRoundtrip() + testBytesRoundtrip() + testEmptyRoundtrip() + testOptionRoundtrip() + testRecordRoundtrip() + testInlineRoundtrip() + testLargeRoundtrip() + testMultiParamRoundtrip() + testNestedRoundtrip() + testVariantRoundtrip() + testResultRoundtrip() + testTupleRoundtrip() + testSingleEntryRoundtrip() +} + +func testNamedRoundtrip() { + input := test.NamesById{ + 1: "uno", + 2: "two", + } + result := test.NamedRoundtrip(input) + assertEqual(result["uno"], uint32(1)) + assertEqual(result["two"], uint32(2)) +} + +func testBytesRoundtrip() { + input := test.BytesByName{ + "hello": []uint8("world"), + "bin": {0, 1, 2}, + } + result := test.BytesRoundtrip(input) + assertSliceEqual(result["hello"], []uint8("world")) + assertSliceEqual(result["bin"], []uint8{0, 1, 2}) +} + +func testEmptyRoundtrip() { + input := test.NamesById{} + result := test.EmptyRoundtrip(input) + assertEqual(len(result), 0) +} + +func testOptionRoundtrip() { + input := map[string]Option[uint32]{ + "some": Some[uint32](42), + "none": None[uint32](), + } + result := test.OptionRoundtrip(input) + assertEqual(len(result), 2) + assertEqual(result["some"].Some(), uint32(42)) + assertEqual(result["none"].Tag(), OptionNone) +} + +func testRecordRoundtrip() { + entry := test.LabeledEntry{ + Label: "test-label", + Values: test.NamesById{ + 10: "ten", + 20: "twenty", + }, + } + result := test.RecordRoundtrip(entry) + assertEqual(result.Label, "test-label") + assertEqual(len(result.Values), 2) + assertEqual(result.Values[10], "ten") + assertEqual(result.Values[20], "twenty") +} + +func testInlineRoundtrip() { + input := map[uint32]string{ + 1: "one", + 2: "two", + } + result := test.InlineRoundtrip(input) + assertEqual(len(result), 2) + assertEqual(result["one"], uint32(1)) + assertEqual(result["two"], uint32(2)) +} + +func testLargeRoundtrip() { + input := make(test.NamesById) + for i := uint32(0); i < 100; i++ { + input[i] = fmt.Sprintf("value-%d", i) + } + result := test.LargeRoundtrip(input) + assertEqual(len(result), 100) + for i := uint32(0); i < 100; i++ { + assertEqual(result[i], fmt.Sprintf("value-%d", i)) + } +} + +func testMultiParamRoundtrip() { + names := test.NamesById{ + 1: "one", + 2: "two", + } + bytes := test.BytesByName{ + "key": {42}, + } + ids, bytesOut := test.MultiParamRoundtrip(names, bytes) + assertEqual(len(ids), 2) + assertEqual(ids["one"], uint32(1)) + assertEqual(ids["two"], uint32(2)) + assertEqual(len(bytesOut), 1) + assertSliceEqual(bytesOut["key"], []uint8{42}) +} + +func testNestedRoundtrip() { + input := map[string]map[uint32]string{ + "group-a": { + 1: "one", + 2: "two", + }, + "group-b": { + 10: "ten", + }, + } + result := test.NestedRoundtrip(input) + assertEqual(len(result), 2) + assertEqual(result["group-a"][1], "one") + assertEqual(result["group-a"][2], "two") + assertEqual(result["group-b"][10], "ten") +} + +func testVariantRoundtrip() { + m := test.NamesById{1: "one"} + asMap := test.VariantRoundtrip(test.MakeMapOrStringAsMap(m)) + assertEqual(asMap.Tag(), test.MapOrStringAsMap) + assertEqual(asMap.AsMap()[1], "one") + + asStr := test.VariantRoundtrip(test.MakeMapOrStringAsString("hello")) + assertEqual(asStr.Tag(), test.MapOrStringAsString) + assertEqual(asStr.AsString(), "hello") +} + +func testResultRoundtrip() { + m := test.NamesById{5: "five"} + okResult := test.ResultRoundtrip(Ok[test.NamesById, string](m)) + assertEqual(okResult.Tag(), ResultOk) + assertEqual(okResult.Ok()[5], "five") + + errResult := test.ResultRoundtrip(Err[test.NamesById, string]("bad input")) + assertEqual(errResult.Tag(), ResultErr) + assertEqual(errResult.Err(), "bad input") +} + +func testTupleRoundtrip() { + m := test.NamesById{7: "seven"} + resultMap, resultNum := test.TupleRoundtrip(Tuple2[test.NamesById, uint64]{m, 42}) + assertEqual(len(resultMap), 1) + assertEqual(resultMap[7], "seven") + assertEqual(resultNum, uint64(42)) +} + +func testSingleEntryRoundtrip() { + input := test.NamesById{99: "ninety-nine"} + result := test.SingleEntryRoundtrip(input) + assertEqual(len(result), 1) + assertEqual(result[99], "ninety-nine") +} + +func assertEqual[T comparable](a T, b T) { + if a != b { + panic(fmt.Sprintf("%v not equal to %v", a, b)) + } +} + +func assertSliceEqual[T comparable](a []T, b []T) { + if len(a) != len(b) { + panic(fmt.Sprintf("slices have different lengths: %d vs %d", len(a), len(b))) + } + for i := range a { + if a[i] != b[i] { + panic(fmt.Sprintf("slices differ at index %d: %v vs %v", i, a[i], b[i])) + } + } +} diff --git a/tests/runtime/map/runner.rs b/tests/runtime/map/runner.rs new file mode 100644 index 000000000..a39014bab --- /dev/null +++ b/tests/runtime/map/runner.rs @@ -0,0 +1,198 @@ +//@ wasmtime-flags = '-Wcomponent-model-map' + +include!(env!("BINDINGS")); + +use test::maps::to_test::*; + +struct Component; + +export!(Component); + +impl Guest for Component { + fn run() { + test_named_roundtrip(); + test_bytes_roundtrip(); + test_empty_roundtrip(); + test_option_roundtrip(); + test_record_roundtrip(); + test_inline_roundtrip(); + test_large_map(); + test_multi_param_roundtrip(); + test_nested_roundtrip(); + test_variant_roundtrip(); + test_result_roundtrip(); + test_tuple_roundtrip(); + test_single_entry_roundtrip(); + } +} + +fn test_named_roundtrip() { + let mut input = NamesById::new(); + input.insert(1, "one".to_string()); + input.insert(1, "uno".to_string()); + input.insert(2, "two".to_string()); + let ids_by_name = named_roundtrip(&input); + assert_eq!(ids_by_name.get("uno"), Some(&1)); + assert_eq!(ids_by_name.get("two"), Some(&2)); + assert_eq!(ids_by_name.get("one"), None); +} + +fn test_bytes_roundtrip() { + let mut bytes_input = BytesByName::new(); + bytes_input.insert("hello".to_string(), b"world".to_vec()); + bytes_input.insert("bin".to_string(), vec![0u8, 1, 2]); + let bytes_by_name = bytes_roundtrip(&bytes_input); + assert_eq!( + bytes_by_name.get("hello").map(Vec::as_slice), + Some(b"world".as_slice()) + ); + assert_eq!( + bytes_by_name.get("bin").map(Vec::as_slice), + Some([0u8, 1, 2].as_slice()) + ); +} + +fn test_empty_roundtrip() { + let empty = NamesById::new(); + let result = empty_roundtrip(&empty); + assert!(result.is_empty()); +} + +fn test_option_roundtrip() { + let mut input = wit_bindgen::rt::Map::new(); + input.insert("some".to_string(), Some(42)); + input.insert("none".to_string(), None); + let result = option_roundtrip(&input); + assert_eq!(result.len(), 2); + assert_eq!(result.get("some"), Some(&Some(42))); + assert_eq!(result.get("none"), Some(&None)); +} + +fn test_record_roundtrip() { + let mut values = NamesById::new(); + values.insert(10, "ten".to_string()); + values.insert(20, "twenty".to_string()); + let entry = LabeledEntry { + label: "test-label".to_string(), + values, + }; + let result = record_roundtrip(&entry); + assert_eq!(result.label, "test-label"); + assert_eq!(result.values.len(), 2); + assert_eq!(result.values.get(&10).map(String::as_str), Some("ten")); + assert_eq!(result.values.get(&20).map(String::as_str), Some("twenty")); +} + +fn test_inline_roundtrip() { + let mut input = wit_bindgen::rt::Map::new(); + input.insert(1, "one".to_string()); + input.insert(2, "two".to_string()); + let result = inline_roundtrip(&input); + assert_eq!(result.len(), 2); + assert_eq!(result.get("one"), Some(&1)); + assert_eq!(result.get("two"), Some(&2)); +} + +fn test_large_map() { + let mut input = NamesById::new(); + for i in 0..100 { + input.insert(i, format!("value-{i}")); + } + let result = large_roundtrip(&input); + assert_eq!(result.len(), 100); + for i in 0..100 { + assert_eq!( + result.get(&i).map(String::as_str), + Some(format!("value-{i}").as_str()), + ); + } +} + +fn test_multi_param_roundtrip() { + let mut names = NamesById::new(); + names.insert(1, "one".to_string()); + names.insert(2, "two".to_string()); + let mut bytes = BytesByName::new(); + bytes.insert("key".to_string(), vec![42u8]); + let (ids, bytes_out) = multi_param_roundtrip(&names, &bytes); + assert_eq!(ids.len(), 2); + assert_eq!(ids.get("one"), Some(&1)); + assert_eq!(ids.get("two"), Some(&2)); + assert_eq!(bytes_out.len(), 1); + assert_eq!( + bytes_out.get("key").map(Vec::as_slice), + Some([42u8].as_slice()), + ); +} + +fn test_nested_roundtrip() { + let mut inner_a = wit_bindgen::rt::Map::new(); + inner_a.insert(1, "one".to_string()); + inner_a.insert(2, "two".to_string()); + let mut inner_b = wit_bindgen::rt::Map::new(); + inner_b.insert(10, "ten".to_string()); + let mut outer = wit_bindgen::rt::Map::new(); + outer.insert("group-a".to_string(), inner_a); + outer.insert("group-b".to_string(), inner_b); + let result = nested_roundtrip(&outer); + assert_eq!(result.len(), 2); + let ra = result.get("group-a").unwrap(); + assert_eq!(ra.get(&1).map(String::as_str), Some("one")); + assert_eq!(ra.get(&2).map(String::as_str), Some("two")); + let rb = result.get("group-b").unwrap(); + assert_eq!(rb.get(&10).map(String::as_str), Some("ten")); +} + +fn test_variant_roundtrip() { + let mut map = NamesById::new(); + map.insert(1, "one".to_string()); + let as_map = variant_roundtrip(&MapOrString::AsMap(map)); + match &as_map { + MapOrString::AsMap(m) => { + assert_eq!(m.get(&1).map(String::as_str), Some("one")); + } + MapOrString::AsString(_) => panic!("expected AsMap"), + } + + let as_str = variant_roundtrip(&MapOrString::AsString("hello".to_string())); + match &as_str { + MapOrString::AsString(s) => assert_eq!(s, "hello"), + MapOrString::AsMap(_) => panic!("expected AsString"), + } +} + +fn test_result_roundtrip() { + let mut map = NamesById::new(); + map.insert(5, "five".to_string()); + let ok_result = result_roundtrip(Ok(&map)); + match &ok_result { + Ok(m) => assert_eq!(m.get(&5).map(String::as_str), Some("five")), + Err(_) => panic!("expected Ok"), + } + + let err_result = result_roundtrip(Err("bad input")); + match &err_result { + Err(e) => assert_eq!(e, "bad input"), + Ok(_) => panic!("expected Err"), + } +} + +fn test_tuple_roundtrip() { + let mut map = NamesById::new(); + map.insert(7, "seven".to_string()); + let (result_map, result_num) = tuple_roundtrip((&map, 42)); + assert_eq!(result_map.len(), 1); + assert_eq!(result_map.get(&7).map(String::as_str), Some("seven")); + assert_eq!(result_num, 42); +} + +fn test_single_entry_roundtrip() { + let mut input = NamesById::new(); + input.insert(99, "ninety-nine".to_string()); + let result = single_entry_roundtrip(&input); + assert_eq!(result.len(), 1); + assert_eq!( + result.get(&99).map(String::as_str), + Some("ninety-nine"), + ); +} diff --git a/tests/runtime/map/test.go b/tests/runtime/map/test.go new file mode 100644 index 000000000..80f7b96ac --- /dev/null +++ b/tests/runtime/map/test.go @@ -0,0 +1,71 @@ +package export_test_maps_to_test + +import ( + . "wit_component/test_maps_to_test" + + . "go.bytecodealliance.org/pkg/wit/types" +) + +func NamedRoundtrip(a NamesById) IdsByName { + result := make(IdsByName) + for id, name := range a { + result[name] = id + } + return result +} + +func BytesRoundtrip(a BytesByName) BytesByName { + return a +} + +func EmptyRoundtrip(a NamesById) NamesById { + return a +} + +func OptionRoundtrip(a map[string]Option[uint32]) map[string]Option[uint32] { + return a +} + +func RecordRoundtrip(a LabeledEntry) LabeledEntry { + return a +} + +func InlineRoundtrip(a map[uint32]string) map[string]uint32 { + result := make(map[string]uint32) + for k, v := range a { + result[v] = k + } + return result +} + +func LargeRoundtrip(a NamesById) NamesById { + return a +} + +func MultiParamRoundtrip(a NamesById, b BytesByName) (IdsByName, BytesByName) { + ids := make(IdsByName) + for id, name := range a { + ids[name] = id + } + return ids, b +} + +func NestedRoundtrip(a map[string]map[uint32]string) map[string]map[uint32]string { + return a +} + +func VariantRoundtrip(a MapOrString) MapOrString { + return a +} + +func ResultRoundtrip(a Result[NamesById, string]) Result[NamesById, string] { + return a +} + +func TupleRoundtrip(a Tuple2[NamesById, uint64]) (NamesById, uint64) { + return a.F0, a.F1 +} + +func SingleEntryRoundtrip(a NamesById) NamesById { + return a +} diff --git a/tests/runtime/map/test.rs b/tests/runtime/map/test.rs new file mode 100644 index 000000000..efee67c86 --- /dev/null +++ b/tests/runtime/map/test.rs @@ -0,0 +1,111 @@ +include!(env!("BINDINGS")); + +use crate::exports::test::maps::to_test::{ + BytesByName, IdsByName, LabeledEntry, MapOrString, NamesById, +}; + +struct Component; + +export!(Component); + +impl exports::test::maps::to_test::Guest for Component { + fn named_roundtrip(a: NamesById) -> IdsByName { + assert_eq!(a.get(&1).map(String::as_str), Some("uno")); + assert_eq!(a.get(&2).map(String::as_str), Some("two")); + + let mut result = IdsByName::new(); + for (id, name) in a { + result.insert(name, id); + } + result + } + + fn bytes_roundtrip(a: BytesByName) -> BytesByName { + assert_eq!( + a.get("hello").map(Vec::as_slice), + Some(b"world".as_slice()) + ); + assert_eq!( + a.get("bin").map(Vec::as_slice), + Some([0u8, 1, 2].as_slice()) + ); + a + } + + fn empty_roundtrip(a: NamesById) -> NamesById { + assert!(a.is_empty()); + a + } + + fn option_roundtrip( + a: wit_bindgen::rt::Map>, + ) -> wit_bindgen::rt::Map> { + assert_eq!(a.get("some"), Some(&Some(42))); + assert_eq!(a.get("none"), Some(&None)); + a + } + + fn record_roundtrip(a: LabeledEntry) -> LabeledEntry { + assert_eq!(a.label, "test-label"); + assert_eq!(a.values.len(), 2); + assert_eq!(a.values.get(&10).map(String::as_str), Some("ten")); + assert_eq!(a.values.get(&20).map(String::as_str), Some("twenty")); + a + } + + fn inline_roundtrip( + a: wit_bindgen::rt::Map, + ) -> wit_bindgen::rt::Map { + let mut result = wit_bindgen::rt::Map::new(); + for (k, v) in a { + result.insert(v, k); + } + result + } + + fn large_roundtrip(a: NamesById) -> NamesById { + a + } + + fn multi_param_roundtrip(a: NamesById, b: BytesByName) -> (IdsByName, BytesByName) { + assert_eq!(a.len(), 2); + assert_eq!(b.len(), 1); + let mut ids = IdsByName::new(); + for (id, name) in a { + ids.insert(name, id); + } + (ids, b) + } + + fn nested_roundtrip( + a: wit_bindgen::rt::Map>, + ) -> wit_bindgen::rt::Map> { + assert_eq!(a.len(), 2); + let inner = a.get("group-a").unwrap(); + assert_eq!(inner.get(&1).map(String::as_str), Some("one")); + assert_eq!(inner.get(&2).map(String::as_str), Some("two")); + let inner2 = a.get("group-b").unwrap(); + assert_eq!(inner2.get(&10).map(String::as_str), Some("ten")); + a + } + + fn variant_roundtrip(a: MapOrString) -> MapOrString { + a + } + + fn result_roundtrip(a: Result) -> Result { + a + } + + fn tuple_roundtrip(a: (NamesById, u64)) -> (NamesById, u64) { + assert_eq!(a.0.len(), 1); + assert_eq!(a.0.get(&7).map(String::as_str), Some("seven")); + assert_eq!(a.1, 42); + a + } + + fn single_entry_roundtrip(a: NamesById) -> NamesById { + assert_eq!(a.len(), 1); + a + } +} diff --git a/tests/runtime/map/test.wit b/tests/runtime/map/test.wit new file mode 100644 index 000000000..4822d3426 --- /dev/null +++ b/tests/runtime/map/test.wit @@ -0,0 +1,41 @@ +package test:maps; + +interface to-test { + type names-by-id = map; + type ids-by-name = map; + type bytes-by-name = map>; + + record labeled-entry { + label: string, + values: names-by-id, + } + + variant map-or-string { + as-map(names-by-id), + as-string(string), + } + + named-roundtrip: func(a: names-by-id) -> ids-by-name; + bytes-roundtrip: func(a: bytes-by-name) -> bytes-by-name; + empty-roundtrip: func(a: names-by-id) -> names-by-id; + option-roundtrip: func(a: map>) -> map>; + record-roundtrip: func(a: labeled-entry) -> labeled-entry; + inline-roundtrip: func(a: map) -> map; + large-roundtrip: func(a: names-by-id) -> names-by-id; + multi-param-roundtrip: func(a: names-by-id, b: bytes-by-name) -> tuple; + nested-roundtrip: func(a: map>) -> map>; + variant-roundtrip: func(a: map-or-string) -> map-or-string; + result-roundtrip: func(a: result) -> result; + tuple-roundtrip: func(a: tuple) -> tuple; + single-entry-roundtrip: func(a: names-by-id) -> names-by-id; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/numbers/runner.go b/tests/runtime/numbers/runner.go new file mode 100644 index 000000000..54d96e7f0 --- /dev/null +++ b/tests/runtime/numbers/runner.go @@ -0,0 +1,90 @@ +package runner + +import ( + "context" + "fmt" + "math" + + wrpc "wrpc.io/go" + "driver/runner/test/numbers/numbers" +) + +func Run(ctx context.Context, c wrpc.Invoker) error { + assertEqual(must(numbers.RoundtripU8(ctx, c, 1)), 1) + assertEqual(must(numbers.RoundtripU8(ctx, c, 0)), 0) + assertEqual(must(numbers.RoundtripU8(ctx, c, math.MaxUint8)), math.MaxUint8) + + assertEqual(must(numbers.RoundtripS8(ctx, c, 1)), 1) + assertEqual(must(numbers.RoundtripS8(ctx, c, math.MinInt8)), math.MinInt8) + assertEqual(must(numbers.RoundtripS8(ctx, c, math.MaxInt8)), math.MaxInt8) + + assertEqual(must(numbers.RoundtripU16(ctx, c, 1)), 1) + assertEqual(must(numbers.RoundtripU16(ctx, c, 0)), 0) + assertEqual(must(numbers.RoundtripU16(ctx, c, math.MaxUint16)), math.MaxUint16) + + assertEqual(must(numbers.RoundtripS16(ctx, c, 1)), 1) + assertEqual(must(numbers.RoundtripS16(ctx, c, math.MinInt16)), math.MinInt16) + assertEqual(must(numbers.RoundtripS16(ctx, c, math.MaxInt16)), math.MaxInt16) + + assertEqual(must(numbers.RoundtripU32(ctx, c, 1)), 1) + assertEqual(must(numbers.RoundtripU32(ctx, c, 0)), 0) + assertEqual(must(numbers.RoundtripU32(ctx, c, math.MaxUint32)), math.MaxUint32) + + assertEqual(must(numbers.RoundtripS32(ctx, c, 1)), 1) + assertEqual(must(numbers.RoundtripS32(ctx, c, math.MinInt32)), math.MinInt32) + assertEqual(must(numbers.RoundtripS32(ctx, c, math.MaxInt32)), math.MaxInt32) + + assertEqual(must(numbers.RoundtripU64(ctx, c, 1)), 1) + assertEqual(must(numbers.RoundtripU64(ctx, c, 0)), 0) + assertEqual(must(numbers.RoundtripU64(ctx, c, math.MaxUint64)), math.MaxUint64) + + assertEqual(must(numbers.RoundtripS64(ctx, c, 1)), 1) + assertEqual(must(numbers.RoundtripS64(ctx, c, math.MinInt64)), math.MinInt64) + assertEqual(must(numbers.RoundtripS64(ctx, c, math.MaxInt64)), math.MaxInt64) + + assertEqual(must(numbers.RoundtripF32(ctx, c, 1.0)), 1.0) + assertEqual(must(numbers.RoundtripF32(ctx, c, float32(math.Inf(1)))), float32(math.Inf(1))) + assertEqual(must(numbers.RoundtripF32(ctx, c, float32(math.Inf(-1)))), float32(math.Inf(-1))) + assert(math.IsNaN(float64(must(numbers.RoundtripF32(ctx, c, float32(math.NaN())))))) + + assertEqual(must(numbers.RoundtripF64(ctx, c, 1.0)), 1.0) + assertEqual(must(numbers.RoundtripF64(ctx, c, math.Inf(1))), math.Inf(1)) + assertEqual(must(numbers.RoundtripF64(ctx, c, math.Inf(-1))), math.Inf(-1)) + assert(math.IsNaN(must(numbers.RoundtripF64(ctx, c, math.NaN())))) + + assertEqual(must(numbers.RoundtripChar(ctx, c, 'a')), 'a') + assertEqual(must(numbers.RoundtripChar(ctx, c, ' ')), ' ') + assertEqual(must(numbers.RoundtripChar(ctx, c, '🚩')), '🚩') + + must0(numbers.SetScalar(ctx, c, 2)) + assertEqual(must(numbers.GetScalar(ctx, c)), 2) + + must0(numbers.SetScalar(ctx, c, 4)) + assertEqual(must(numbers.GetScalar(ctx, c)), 4) + return nil +} + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +func must0(err error) { + if err != nil { + panic(err) + } +} + +func assertEqual[T comparable](a T, b T) { + if a != b { + panic(fmt.Sprintf("%v not equal to %v", a, b)) + } +} + +func assert(v bool) { + if !v { + panic("assertion failed") + } +} diff --git a/tests/runtime/numbers/runner.rs b/tests/runtime/numbers/runner.rs new file mode 100644 index 000000000..4b56ce378 --- /dev/null +++ b/tests/runtime/numbers/runner.rs @@ -0,0 +1,63 @@ +use crate::runner::test::numbers::numbers::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(roundtrip_u8(wrpc, (), 1).await?, 1); + assert_eq!(roundtrip_u8(wrpc, (), u8::MIN).await?, u8::MIN); + assert_eq!(roundtrip_u8(wrpc, (), u8::MAX).await?, u8::MAX); + + assert_eq!(roundtrip_s8(wrpc, (), 1).await?, 1); + assert_eq!(roundtrip_s8(wrpc, (), i8::MIN).await?, i8::MIN); + assert_eq!(roundtrip_s8(wrpc, (), i8::MAX).await?, i8::MAX); + + assert_eq!(roundtrip_u16(wrpc, (), 1).await?, 1); + assert_eq!(roundtrip_u16(wrpc, (), u16::MIN).await?, u16::MIN); + assert_eq!(roundtrip_u16(wrpc, (), u16::MAX).await?, u16::MAX); + + assert_eq!(roundtrip_s16(wrpc, (), 1).await?, 1); + assert_eq!(roundtrip_s16(wrpc, (), i16::MIN).await?, i16::MIN); + assert_eq!(roundtrip_s16(wrpc, (), i16::MAX).await?, i16::MAX); + + assert_eq!(roundtrip_u32(wrpc, (), 1).await?, 1); + assert_eq!(roundtrip_u32(wrpc, (), u32::MIN).await?, u32::MIN); + assert_eq!(roundtrip_u32(wrpc, (), u32::MAX).await?, u32::MAX); + + assert_eq!(roundtrip_s32(wrpc, (), 1).await?, 1); + assert_eq!(roundtrip_s32(wrpc, (), i32::MIN).await?, i32::MIN); + assert_eq!(roundtrip_s32(wrpc, (), i32::MAX).await?, i32::MAX); + + assert_eq!(roundtrip_u64(wrpc, (), 1).await?, 1); + assert_eq!(roundtrip_u64(wrpc, (), u64::MIN).await?, u64::MIN); + assert_eq!(roundtrip_u64(wrpc, (), u64::MAX).await?, u64::MAX); + + assert_eq!(roundtrip_s64(wrpc, (), 1).await?, 1); + assert_eq!(roundtrip_s64(wrpc, (), i64::MIN).await?, i64::MIN); + assert_eq!(roundtrip_s64(wrpc, (), i64::MAX).await?, i64::MAX); + + assert_eq!(roundtrip_f32(wrpc, (), 1.0).await?, 1.0); + assert_eq!(roundtrip_f32(wrpc, (), f32::INFINITY).await?, f32::INFINITY); + assert_eq!( + roundtrip_f32(wrpc, (), f32::NEG_INFINITY).await?, + f32::NEG_INFINITY + ); + assert!(roundtrip_f32(wrpc, (), f32::NAN).await?.is_nan()); + + assert_eq!(roundtrip_f64(wrpc, (), 1.0).await?, 1.0); + assert_eq!(roundtrip_f64(wrpc, (), f64::INFINITY).await?, f64::INFINITY); + assert_eq!( + roundtrip_f64(wrpc, (), f64::NEG_INFINITY).await?, + f64::NEG_INFINITY + ); + assert!(roundtrip_f64(wrpc, (), f64::NAN).await?.is_nan()); + + assert_eq!(roundtrip_char(wrpc, (), 'a').await?, 'a'); + assert_eq!(roundtrip_char(wrpc, (), ' ').await?, ' '); + assert_eq!(roundtrip_char(wrpc, (), '🚩').await?, '🚩'); + + set_scalar(wrpc, (), 2).await?; + assert_eq!(get_scalar(wrpc, ()).await?, 2); + set_scalar(wrpc, (), 4).await?; + assert_eq!(get_scalar(wrpc, ()).await?, 4); + Ok(()) +} diff --git a/tests/runtime/numbers/test.go b/tests/runtime/numbers/test.go new file mode 100644 index 000000000..6bba51f8d --- /dev/null +++ b/tests/runtime/numbers/test.go @@ -0,0 +1,64 @@ +package test + +import "context" + +type handler struct{} + +func NewHandler() handler { + return handler{} +} + +func (handler) RoundtripU8(ctx context.Context, v uint8) (uint8, error) { + return v, nil +} + +func (handler) RoundtripS8(ctx context.Context, v int8) (int8, error) { + return v, nil +} + +func (handler) RoundtripU16(ctx context.Context, v uint16) (uint16, error) { + return v, nil +} + +func (handler) RoundtripS16(ctx context.Context, v int16) (int16, error) { + return v, nil +} + +func (handler) RoundtripU32(ctx context.Context, v uint32) (uint32, error) { + return v, nil +} + +func (handler) RoundtripS32(ctx context.Context, v int32) (int32, error) { + return v, nil +} + +func (handler) RoundtripU64(ctx context.Context, v uint64) (uint64, error) { + return v, nil +} + +func (handler) RoundtripS64(ctx context.Context, v int64) (int64, error) { + return v, nil +} + +func (handler) RoundtripF32(ctx context.Context, v float32) (float32, error) { + return v, nil +} + +func (handler) RoundtripF64(ctx context.Context, v float64) (float64, error) { + return v, nil +} + +func (handler) RoundtripChar(ctx context.Context, v rune) (rune, error) { + return v, nil +} + +var scalar uint32 = 0 + +func (handler) SetScalar(ctx context.Context, v uint32) error { + scalar = v + return nil +} + +func (handler) GetScalar(ctx context.Context) (uint32, error) { + return scalar, nil +} diff --git a/tests/runtime/numbers/test.rs b/tests/runtime/numbers/test.rs new file mode 100644 index 000000000..400cdeb20 --- /dev/null +++ b/tests/runtime/numbers/test.rs @@ -0,0 +1,61 @@ +use std::sync::atomic::{AtomicU32, Ordering::SeqCst}; + +#[derive(Clone)] +pub struct Component; + +static SCALAR: AtomicU32 = AtomicU32::new(0); + +impl crate::test::exports::test::numbers::numbers::Handler for Component { + async fn roundtrip_u8(&self, _cx: Ctx, a: u8) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_s8(&self, _cx: Ctx, a: i8) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_u16(&self, _cx: Ctx, a: u16) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_s16(&self, _cx: Ctx, a: i16) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_u32(&self, _cx: Ctx, a: u32) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_s32(&self, _cx: Ctx, a: i32) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_u64(&self, _cx: Ctx, a: u64) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_s64(&self, _cx: Ctx, a: i64) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_f32(&self, _cx: Ctx, a: f32) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_f64(&self, _cx: Ctx, a: f64) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_char(&self, _cx: Ctx, a: char) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn set_scalar(&self, _cx: Ctx, val: u32) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + SCALAR.store(val, SeqCst); + Ok(()) + } + + async fn get_scalar(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(SCALAR.load(SeqCst)) + } +} diff --git a/tests/runtime/numbers/test.wit b/tests/runtime/numbers/test.wit new file mode 100644 index 000000000..359a9bc71 --- /dev/null +++ b/tests/runtime/numbers/test.wit @@ -0,0 +1,28 @@ +package test:numbers; + +interface numbers { + roundtrip-u8: func(a: u8) -> u8; + roundtrip-s8: func(a: s8) -> s8; + roundtrip-u16: func(a: u16) -> u16; + roundtrip-s16: func(a: s16) -> s16; + roundtrip-u32: func(a: u32) -> u32; + roundtrip-s32: func(a: s32) -> s32; + roundtrip-u64: func(a: u64) -> u64; + roundtrip-s64: func(a: s64) -> s64; + roundtrip-f32: func(a: f32) -> f32; + roundtrip-f64: func(a: f64) -> f64; + roundtrip-char: func(a: char) -> char; + + set-scalar: func(a: u32); + get-scalar: func() -> u32; +} + +world test { + export numbers; +} + +world runner { + import numbers; + + export run: func(); +} diff --git a/tests/runtime/options/runner.rs b/tests/runtime/options/runner.rs new file mode 100644 index 000000000..775123ca0 --- /dev/null +++ b/tests/runtime/options/runner.rs @@ -0,0 +1,24 @@ +use crate::runner::test::options::to_test::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + option_none_param(wrpc, (), None).await?; + option_some_param(wrpc, (), Some("foo")).await?; + assert!(option_none_result(wrpc, ()).await?.is_none()); + assert_eq!(option_some_result(wrpc, ()).await?, Some("foo".to_string())); + assert_eq!( + option_roundtrip(wrpc, (), Some("foo")).await?, + Some("foo".to_string()) + ); + assert_eq!( + double_option_roundtrip(wrpc, (), Some(Some(42))).await?, + Some(Some(42)) + ); + assert_eq!( + double_option_roundtrip(wrpc, (), Some(None)).await?, + Some(None) + ); + assert_eq!(double_option_roundtrip(wrpc, (), None).await?, None); + Ok(()) +} diff --git a/tests/runtime/options/test.rs b/tests/runtime/options/test.rs new file mode 100644 index 000000000..8788f3890 --- /dev/null +++ b/tests/runtime/options/test.rs @@ -0,0 +1,52 @@ +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::test::options::to_test::Handler for Component { + async fn option_none_param( + &self, + _cx: Ctx, + a: Option, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert!(a.is_none()); + Ok(()) + } + + async fn option_none_result( + &self, + _cx: Ctx, + ) -> ::wit_bindgen_wrpc::anyhow::Result> { + Ok(None) + } + + async fn option_some_param( + &self, + _cx: Ctx, + a: Option, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(a, Some("foo".to_string())); + Ok(()) + } + + async fn option_some_result( + &self, + _cx: Ctx, + ) -> ::wit_bindgen_wrpc::anyhow::Result> { + Ok(Some("foo".to_string())) + } + + async fn option_roundtrip( + &self, + _cx: Ctx, + a: Option, + ) -> ::wit_bindgen_wrpc::anyhow::Result> { + Ok(a) + } + + async fn double_option_roundtrip( + &self, + _cx: Ctx, + a: Option>, + ) -> ::wit_bindgen_wrpc::anyhow::Result>> { + Ok(a) + } +} diff --git a/tests/runtime/options/test.wit b/tests/runtime/options/test.wit new file mode 100644 index 000000000..9142ed459 --- /dev/null +++ b/tests/runtime/options/test.wit @@ -0,0 +1,22 @@ +package test:options; + +interface to-test { + option-none-param: func(a: option); + option-some-param: func(a: option); + option-none-result: func() -> option; + option-some-result: func() -> option; + + option-roundtrip: func(a: option) -> option; + + double-option-roundtrip: func(a: option>) -> option>; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/package-with-version/runner.rs b/tests/runtime/package-with-version/runner.rs new file mode 100644 index 000000000..501696d53 --- /dev/null +++ b/tests/runtime/package-with-version/runner.rs @@ -0,0 +1,6 @@ +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let _ = crate::runner::my::inline::foo::Bar::new(wrpc, ()).await?; + Ok(()) +} diff --git a/tests/runtime/package-with-version/test.rs b/tests/runtime/package-with-version/test.rs new file mode 100644 index 000000000..0b8fdd2df --- /dev/null +++ b/tests/runtime/package-with-version/test.rs @@ -0,0 +1,19 @@ +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::my::inline::foo::Handler for Component {} + +impl crate::test::exports::my::inline::foo::HandlerBar for Component { + async fn new( + &self, + _cx: Ctx, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn< + crate::test::exports::my::inline::foo::Bar, + >, + > { + Ok(::wit_bindgen_wrpc::wrpc_transport::ResourceOwn::from( + ::wit_bindgen_wrpc::bytes::Bytes::new(), + )) + } +} diff --git a/tests/runtime/package-with-version/test.wit b/tests/runtime/package-with-version/test.wit new file mode 100644 index 000000000..15214ce28 --- /dev/null +++ b/tests/runtime/package-with-version/test.wit @@ -0,0 +1,16 @@ +package my:inline@0.0.0; + +interface foo { + resource bar { + constructor(); + } +} + +world test { + export foo; +} +world runner { + import foo; + + export run: func(); +} diff --git a/tests/runtime/records/runner.go b/tests/runtime/records/runner.go new file mode 100644 index 000000000..1f9bbd6ae --- /dev/null +++ b/tests/runtime/records/runner.go @@ -0,0 +1,73 @@ +package runner + +import ( + "context" + "fmt" + + wrpc "wrpc.io/go" + + "driver/runner/test/records/to_test" +) + +func Run(ctx context.Context, c wrpc.Invoker) error { + a, b := must2(to_test.MultipleResults(ctx, c)) + assertEqual(a, 4) + assertEqual(b, 5) + + c0, c1 := must2(to_test.SwapTuple(ctx, c, &wrpc.Tuple2[uint8, uint32]{V0: 1, V1: 2})) + assertEqual(c0, 2) + assertEqual(c1, 1) + + assertEqual(*must(to_test.RoundtripFlags1(ctx, c, &to_test.F1{A: true})), to_test.F1{A: true}) + assertEqual(*must(to_test.RoundtripFlags1(ctx, c, &to_test.F1{})), to_test.F1{}) + assertEqual(*must(to_test.RoundtripFlags1(ctx, c, &to_test.F1{B: true})), to_test.F1{B: true}) + assertEqual(*must(to_test.RoundtripFlags1(ctx, c, &to_test.F1{A: true, B: true})), to_test.F1{A: true, B: true}) + + assertEqual(*must(to_test.RoundtripFlags2(ctx, c, &to_test.F2{C: true})), to_test.F2{C: true}) + assertEqual(*must(to_test.RoundtripFlags2(ctx, c, &to_test.F2{})), to_test.F2{}) + assertEqual(*must(to_test.RoundtripFlags2(ctx, c, &to_test.F2{D: true})), to_test.F2{D: true}) + assertEqual(*must(to_test.RoundtripFlags2(ctx, c, &to_test.F2{C: true, E: true})), to_test.F2{C: true, E: true}) + + f8, f16, f32 := must3(to_test.RoundtripFlags3(ctx, c, &to_test.Flag8{B0: true}, &to_test.Flag16{B1: true}, &to_test.Flag32{B2: true})) + assertEqual(*f8, to_test.Flag8{B0: true}) + assertEqual(*f16, to_test.Flag16{B1: true}) + assertEqual(*f32, to_test.Flag32{B2: true}) + + r := must(to_test.RoundtripRecord1(ctx, c, &to_test.R1{A: 8, B: &to_test.F1{}})) + assertEqual(r.A, 8) + assertEqual(*r.B, to_test.F1{}) + + r = must(to_test.RoundtripRecord1(ctx, c, &to_test.R1{A: 0, B: &to_test.F1{A: true, B: true}})) + assertEqual(r.A, 0) + assertEqual(*r.B, to_test.F1{A: true, B: true}) + + assertEqual(must(to_test.Tuple1(ctx, c, 1)), 1) + return nil +} + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +func must2[A, B any](a A, b B, err error) (A, B) { + if err != nil { + panic(err) + } + return a, b +} + +func must3[A, B, C any](a A, b B, c C, err error) (A, B, C) { + if err != nil { + panic(err) + } + return a, b, c +} + +func assertEqual[T comparable](a T, b T) { + if a != b { + panic(fmt.Sprintf("%v not equal to %v", a, b)) + } +} diff --git a/tests/runtime/records/runner.rs b/tests/runtime/records/runner.rs new file mode 100644 index 000000000..34fb45e9f --- /dev/null +++ b/tests/runtime/records/runner.rs @@ -0,0 +1,56 @@ +use crate::runner::test::records::to_test::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(multiple_results(wrpc, ()).await?, (4, 5)); + + assert_eq!(swap_tuple(wrpc, (), (1u8, 2u32)).await?, (2u32, 1u8)); + assert_eq!(roundtrip_flags1(wrpc, (), &F1::A).await?, F1::A); + assert_eq!(roundtrip_flags1(wrpc, (), &F1::empty()).await?, F1::empty()); + assert_eq!(roundtrip_flags1(wrpc, (), &F1::B).await?, F1::B); + assert_eq!( + roundtrip_flags1(wrpc, (), &(F1::A | F1::B)).await?, + F1::A | F1::B + ); + + assert_eq!(roundtrip_flags2(wrpc, (), &F2::C).await?, F2::C); + assert_eq!(roundtrip_flags2(wrpc, (), &F2::empty()).await?, F2::empty()); + assert_eq!(roundtrip_flags2(wrpc, (), &F2::D).await?, F2::D); + assert_eq!( + roundtrip_flags2(wrpc, (), &(F2::C | F2::E)).await?, + F2::C | F2::E + ); + + assert_eq!( + roundtrip_flags3(wrpc, (), &Flag8::B0, &Flag16::B1, &Flag32::B2).await?, + (Flag8::B0, Flag16::B1, Flag32::B2) + ); + + let r = roundtrip_record1( + wrpc, + (), + &R1 { + a: 8, + b: F1::empty(), + }, + ) + .await?; + assert_eq!(r.a, 8); + assert_eq!(r.b, F1::empty()); + + let r = roundtrip_record1( + wrpc, + (), + &R1 { + a: 0, + b: F1::A | F1::B, + }, + ) + .await?; + assert_eq!(r.a, 0); + assert_eq!(r.b, F1::A | F1::B); + + assert_eq!(tuple1(wrpc, (), (1,)).await?, (1,)); + Ok(()) +} diff --git a/tests/runtime/records/test.go b/tests/runtime/records/test.go new file mode 100644 index 000000000..a3e3327cf --- /dev/null +++ b/tests/runtime/records/test.go @@ -0,0 +1,43 @@ +package test + +import ( + "context" + + wrpc "wrpc.io/go" + + "driver/test/exports/test/records/to_test" +) + +type handler struct{} + +func NewHandler() handler { + return handler{} +} + +func (handler) MultipleResults(ctx context.Context) (uint8, uint16, error) { + return 4, 5, nil +} + +func (handler) SwapTuple(ctx context.Context, a *wrpc.Tuple2[uint8, uint32]) (uint32, uint8, error) { + return a.V1, a.V0, nil +} + +func (handler) RoundtripFlags1(ctx context.Context, a *to_test.F1) (*to_test.F1, error) { + return a, nil +} + +func (handler) RoundtripFlags2(ctx context.Context, a *to_test.F2) (*to_test.F2, error) { + return a, nil +} + +func (handler) RoundtripFlags3(ctx context.Context, a *to_test.Flag8, b *to_test.Flag16, c *to_test.Flag32) (*to_test.Flag8, *to_test.Flag16, *to_test.Flag32, error) { + return a, b, c, nil +} + +func (handler) RoundtripRecord1(ctx context.Context, a *to_test.R1) (*to_test.R1, error) { + return a, nil +} + +func (handler) Tuple1(ctx context.Context, a uint8) (uint8, error) { + return a, nil +} diff --git a/tests/runtime/records/test.rs b/tests/runtime/records/test.rs new file mode 100644 index 000000000..9ac2b30fb --- /dev/null +++ b/tests/runtime/records/test.rs @@ -0,0 +1,44 @@ +use crate::test::exports::test::records::to_test::*; + +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::test::records::to_test::Handler for Component { + async fn multiple_results(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<(u8, u16)> { + Ok((4, 5)) + } + + async fn swap_tuple( + &self, + _cx: Ctx, + a: (u8, u32), + ) -> ::wit_bindgen_wrpc::anyhow::Result<(u32, u8)> { + Ok((a.1, a.0)) + } + + async fn roundtrip_flags1(&self, _cx: Ctx, a: F1) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_flags2(&self, _cx: Ctx, a: F2) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn roundtrip_flags3( + &self, + _cx: Ctx, + a: Flag8, + b: Flag16, + c: Flag32, + ) -> ::wit_bindgen_wrpc::anyhow::Result<(Flag8, Flag16, Flag32)> { + Ok((a, b, c)) + } + + async fn roundtrip_record1(&self, _cx: Ctx, a: R1) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn tuple1(&self, _cx: Ctx, a: (u8,)) -> ::wit_bindgen_wrpc::anyhow::Result<(u8,)> { + Ok((a.0,)) + } +} diff --git a/tests/runtime/records/test.wit b/tests/runtime/records/test.wit new file mode 100644 index 000000000..28f134944 --- /dev/null +++ b/tests/runtime/records/test.wit @@ -0,0 +1,48 @@ +package test:records; + +interface to-test { + multiple-results: func() -> tuple; + + swap-tuple: func(a: tuple) -> tuple; + + flags f1 { a, b } + roundtrip-flags1: func(a: f1) -> f1; + + flags f2 { c, d, e } + roundtrip-flags2: func(a: f2) -> f2; + + flags flag8 { + b0, b1, b2, b3, b4, b5, b6, b7, + } + + flags flag16 { + b0, b1, b2, b3, b4, b5, b6, b7, + b8, b9, b10, b11, b12, b13, b14, b15, + } + + flags flag32 { + b0, b1, b2, b3, b4, b5, b6, b7, + b8, b9, b10, b11, b12, b13, b14, b15, + b16, b17, b18, b19, b20, b21, b22, b23, + b24, b25, b26, b27, b28, b29, b30, b31, + } + + + roundtrip-flags3: func(a: flag8, b: flag16, c: flag32) -> + tuple; + + record r1 { a: u8, b: f1 } + roundtrip-record1: func(a: r1) -> r1; + + tuple1: func(a: tuple) -> tuple; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/resource-borrow/runner.go b/tests/runtime/resource-borrow/runner.go new file mode 100644 index 000000000..2b8bf14cc --- /dev/null +++ b/tests/runtime/resource-borrow/runner.go @@ -0,0 +1,29 @@ +package runner + +import ( + "context" + "fmt" + + wrpc "wrpc.io/go" + + "driver/runner/test/resource_borrow/to_test" +) + +func Run(ctx context.Context, c wrpc.Invoker) error { + thing := must(to_test.NewThing(ctx, c, 42)) + assertEqual(must(to_test.Foo(ctx, c, thing.Borrow())), 42+1+2) + return nil +} + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +func assertEqual[T comparable](a T, b T) { + if a != b { + panic(fmt.Sprintf("%v not equal to %v", a, b)) + } +} diff --git a/tests/runtime/resource-borrow/runner.rs b/tests/runtime/resource-borrow/runner.rs new file mode 100644 index 000000000..328af149c --- /dev/null +++ b/tests/runtime/resource-borrow/runner.rs @@ -0,0 +1,9 @@ +use crate::runner::test::resource_borrow::to_test::{foo, Thing}; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let thing = Thing::new(wrpc, (), 42).await?; + assert_eq!(foo(wrpc, (), &thing.as_borrow()).await?, 42 + 1 + 2); + Ok(()) +} diff --git a/tests/runtime/resource-borrow/test.go b/tests/runtime/resource-borrow/test.go new file mode 100644 index 000000000..b2a938181 --- /dev/null +++ b/tests/runtime/resource-borrow/test.go @@ -0,0 +1,27 @@ +package test + +import ( + "context" + "encoding/binary" + + wrpc "wrpc.io/go" + + to_test "driver/test/exports/test/resource_borrow/to_test" +) + +type handler struct{} + +func NewHandler() handler { + return handler{} +} + +func (handler) Thing(ctx context.Context, v uint32) (wrpc.Own[to_test.Thing], error) { + buf := make([]byte, 4) + binary.LittleEndian.PutUint32(buf, v+1) + return wrpc.Own[to_test.Thing](buf), nil +} + +func (handler) Foo(ctx context.Context, v wrpc.Borrow[to_test.Thing]) (uint32, error) { + val := binary.LittleEndian.Uint32([]byte(v)) + return val + 2, nil +} diff --git a/tests/runtime/resource-borrow/test.rs b/tests/runtime/resource-borrow/test.rs new file mode 100644 index 000000000..54641a3f7 --- /dev/null +++ b/tests/runtime/resource-borrow/test.rs @@ -0,0 +1,31 @@ +use crate::test::exports::test::resource_borrow::to_test::{Handler, HandlerThing, Thing}; + +#[derive(Clone)] +pub struct Component; + +impl Handler for Component { + async fn foo( + &self, + _cx: Ctx, + v: ::wit_bindgen_wrpc::wrpc_transport::ResourceBorrow, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + let bytes: &[u8] = v.as_ref(); + let val = u32::from_le_bytes(bytes.try_into().unwrap()); + Ok(val + 2) + } +} + +impl HandlerThing for Component { + async fn new( + &self, + _cx: Ctx, + v: u32, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn, + > { + let val = v + 1; + Ok(::wit_bindgen_wrpc::wrpc_transport::ResourceOwn::from( + ::wit_bindgen_wrpc::bytes::Bytes::copy_from_slice(&val.to_le_bytes()), + )) + } +} diff --git a/tests/runtime/resource-borrow/test.wit b/tests/runtime/resource-borrow/test.wit new file mode 100644 index 000000000..f378be2f8 --- /dev/null +++ b/tests/runtime/resource-borrow/test.wit @@ -0,0 +1,19 @@ +package test:resource-borrow; + +interface to-test { + resource thing { + constructor(v: u32); + } + + foo: func(v: borrow) -> u32; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/resource_aggregates/runner.rs b/tests/runtime/resource_aggregates/runner.rs new file mode 100644 index 000000000..27aa7cf7f --- /dev/null +++ b/tests/runtime/resource_aggregates/runner.rs @@ -0,0 +1,52 @@ +use crate::runner::test::resource_aggregates::to_test::{ + foo, Thing, R1, R2, R3, V1, V2, +}; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let thing1 = Thing::new(wrpc, (), 1).await?; + let thing2 = Thing::new(wrpc, (), 2).await?; + let thing6 = Thing::new(wrpc, (), 6).await?; + let thing8 = Thing::new(wrpc, (), 8).await?; + let thing11 = Thing::new(wrpc, (), 11).await?; + let thing12 = Thing::new(wrpc, (), 12).await?; + let thing14 = Thing::new(wrpc, (), 14).await?; + let thing16 = Thing::new(wrpc, (), 16).await?; + + let result = foo( + wrpc, + (), + &R1 { + thing: Thing::new(wrpc, (), 0).await?, + }, + &R2 { + thing: thing1.as_borrow(), + }, + &R3 { + thing1: thing2.as_borrow(), + thing2: Thing::new(wrpc, (), 3).await?, + }, + ( + Thing::new(wrpc, (), 4).await?, + R1 { + thing: Thing::new(wrpc, (), 5).await?, + }, + ), + (thing6.as_borrow(),), + &V1::Thing(Thing::new(wrpc, (), 7).await?), + &V2::Thing(thing8.as_borrow()), + &[ + Thing::new(wrpc, (), 9).await?, + Thing::new(wrpc, (), 10).await?, + ], + &[thing11.as_borrow(), thing12.as_borrow()], + Some(Thing::new(wrpc, (), 13).await?), + Some(thing14.as_borrow()), + &Ok(Thing::new(wrpc, (), 15).await?), + &Ok(thing16.as_borrow()), + ) + .await?; + assert_eq!(result, (0..17).map(|i| i + 1).sum::() + 3); + Ok(()) +} diff --git a/tests/runtime/resource_aggregates/test.rs b/tests/runtime/resource_aggregates/test.rs new file mode 100644 index 000000000..4e12f42b5 --- /dev/null +++ b/tests/runtime/resource_aggregates/test.rs @@ -0,0 +1,80 @@ +use crate::test::exports::test::resource_aggregates::to_test::{ + Handler, HandlerThing, Thing, R1, R2, R3, T1, T2, V1, V2, +}; + +#[derive(Clone)] +pub struct Component; + +fn val(repr: &[u8]) -> u32 { + u32::from_le_bytes(repr.try_into().unwrap()) +} + +impl Handler for Component { + #[allow(clippy::too_many_arguments)] + async fn foo( + &self, + _cx: Ctx, + r1: R1, + r2: R2, + r3: R3, + t1: T1, + t2: T2, + v1: V1, + v2: V2, + l1: Vec<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn>, + l2: Vec<::wit_bindgen_wrpc::wrpc_transport::ResourceBorrow>, + o1: Option<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn>, + o2: Option<::wit_bindgen_wrpc::wrpc_transport::ResourceBorrow>, + result1: Result<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn, ()>, + result2: Result<::wit_bindgen_wrpc::wrpc_transport::ResourceBorrow, ()>, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + let mut sum = 0; + sum += val(r1.thing.as_ref()); + sum += val(r2.thing.as_ref()); + sum += val(r3.thing1.as_ref()); + sum += val(r3.thing2.as_ref()); + sum += val(t1.0.as_ref()); + sum += val(t1.1.thing.as_ref()); + sum += val(t2.0.as_ref()); + match v1 { + V1::Thing(v) => sum += val(v.as_ref()), + } + match v2 { + V2::Thing(v) => sum += val(v.as_ref()), + } + for v in &l1 { + sum += val(v.as_ref()); + } + for v in &l2 { + sum += val(v.as_ref()); + } + if let Some(v) = &o1 { + sum += val(v.as_ref()); + } + if let Some(v) = &o2 { + sum += val(v.as_ref()); + } + if let Ok(v) = &result1 { + sum += val(v.as_ref()); + } + if let Ok(v) = &result2 { + sum += val(v.as_ref()); + } + Ok(sum + 3) + } +} + +impl HandlerThing for Component { + async fn new( + &self, + _cx: Ctx, + v: u32, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn, + > { + let val = v + 1; + Ok(::wit_bindgen_wrpc::wrpc_transport::ResourceOwn::from( + ::wit_bindgen_wrpc::bytes::Bytes::copy_from_slice(&val.to_le_bytes()), + )) + } +} diff --git a/tests/runtime/resource_aggregates/test.wit b/tests/runtime/resource_aggregates/test.wit new file mode 100644 index 000000000..6c8ebc7e6 --- /dev/null +++ b/tests/runtime/resource_aggregates/test.wit @@ -0,0 +1,62 @@ +package test:resource-aggregates; + +interface to-test { + resource thing { + constructor(v: u32); + } + + record r1 { + thing: thing + } + + record r2 { + thing: borrow + } + + record r3 { + thing1: borrow, + thing2: thing, + } + + type t1 = tuple; + + type t2 = tuple>; + + variant v1 { + thing(thing), + } + + variant v2 { + thing(borrow), + } + + type l1 = list; + + type l2 = list>; + + foo: func( + r1: r1, + r2: r2, + r3: r3, + t1: t1, + t2: t2, + v1: v1, + v2: v2, + l1: l1, + l2: l2, + o1: option, + o2: option>, + result1: result, + result2: result>, + ) -> u32; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/resource_alias/runner.rs b/tests/runtime/resource_alias/runner.rs new file mode 100644 index 000000000..9f85cb04f --- /dev/null +++ b/tests/runtime/resource_alias/runner.rs @@ -0,0 +1,17 @@ +use crate::runner::test::resource_alias::e1::{a as a1, Foo as Foo1, X}; +use crate::runner::test::resource_alias::e2::{a as a2, Foo as Foo2}; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let x = X::new(wrpc, (), 42).await?; + a1(wrpc, (), &Foo1 { x }).await?; + + let x = X::new(wrpc, (), 7).await?; + let foo_e2 = Foo2 { x }; + let x = X::new(wrpc, (), 8).await?; + let bar_e2 = Foo1 { x }; + let y = X::new(wrpc, (), 8).await?; + a2(wrpc, (), &foo_e2, &bar_e2, &y.as_borrow()).await?; + Ok(()) +} diff --git a/tests/runtime/resource_alias/test.rs b/tests/runtime/resource_alias/test.rs new file mode 100644 index 000000000..f4ed466c0 --- /dev/null +++ b/tests/runtime/resource_alias/test.rs @@ -0,0 +1,45 @@ +use crate::test::exports::test::resource_alias::e1; +use crate::test::exports::test::resource_alias::e2; + +#[derive(Clone)] +pub struct Component; + +impl e1::Handler for Component { + async fn a( + &self, + _cx: Ctx, + f: e1::Foo, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + Vec<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn>, + > { + Ok(vec![f.x]) + } +} + +impl e1::HandlerX for Component { + async fn new( + &self, + _cx: Ctx, + v: u32, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn, + > { + Ok(::wit_bindgen_wrpc::wrpc_transport::ResourceOwn::from( + ::wit_bindgen_wrpc::bytes::Bytes::copy_from_slice(&v.to_le_bytes()), + )) + } +} + +impl e2::Handler for Component { + async fn a( + &self, + _cx: Ctx, + f: e2::Foo, + g: e2::Bar, + _h: ::wit_bindgen_wrpc::wrpc_transport::ResourceBorrow, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + Vec<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn>, + > { + Ok(vec![f.x, g.x]) + } +} diff --git a/tests/runtime/resource_alias/test.wit b/tests/runtime/resource_alias/test.wit new file mode 100644 index 000000000..d9a1f1aad --- /dev/null +++ b/tests/runtime/resource_alias/test.wit @@ -0,0 +1,31 @@ +package test:resource-alias; + +interface e1 { + resource x { + constructor(v: u32); + } + + record foo { x: x } + + a: func(f: foo) -> list; +} + +interface e2 { + use e1.{x as y, foo as bar}; + + record foo { x: y } + + a: func(f: foo, g: bar, h: borrow) -> list; +} + +world test { + export e1; + export e2; +} + +world runner { + import e1; + import e2; + + export run: func(); +} diff --git a/tests/runtime/resource_alias_redux/runner.rs b/tests/runtime/resource_alias_redux/runner.rs new file mode 100644 index 000000000..5697fd0ec --- /dev/null +++ b/tests/runtime/resource_alias_redux/runner.rs @@ -0,0 +1,44 @@ +use crate::runner::test::resource_alias_redux::resource_alias1 as a1; +use crate::runner::test::resource_alias_redux::resource_alias2 as a2; +use crate::runner::the_test::test; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let thing1 = a1::Thing::new(wrpc, (), "Ni Hao").await?; + let result = test(wrpc, (), &[thing1]).await?; + assert_eq!(result.len(), 1); + assert_eq!( + a1::Thing::get(wrpc, (), &result[0].as_borrow()).await?, + "Ni Hao GuestThing GuestThing.get" + ); + + let thing2 = a1::Thing::new(wrpc, (), "Ciao").await?; + let result = a1::a(wrpc, (), &a1::Foo { thing: thing2 }).await?; + assert_eq!(result.len(), 1); + assert_eq!( + a1::Thing::get(wrpc, (), &result[0].as_borrow()).await?, + "Ciao GuestThing GuestThing.get" + ); + + let thing3 = a1::Thing::new(wrpc, (), "Ciao").await?; + let thing4 = a1::Thing::new(wrpc, (), "Aloha").await?; + + let result = a2::b( + wrpc, + (), + &a2::Foo { thing: thing3 }, + &a1::Foo { thing: thing4 }, + ) + .await?; + assert_eq!(result.len(), 2); + assert_eq!( + a1::Thing::get(wrpc, (), &result[0].as_borrow()).await?, + "Ciao GuestThing GuestThing.get" + ); + assert_eq!( + a1::Thing::get(wrpc, (), &result[1].as_borrow()).await?, + "Aloha GuestThing GuestThing.get" + ); + Ok(()) +} diff --git a/tests/runtime/resource_alias_redux/test.rs b/tests/runtime/resource_alias_redux/test.rs new file mode 100644 index 000000000..c5793203a --- /dev/null +++ b/tests/runtime/resource_alias_redux/test.rs @@ -0,0 +1,68 @@ +use crate::test::exports::test::resource_alias_redux::resource_alias1 as a1; +use crate::test::exports::test::resource_alias_redux::resource_alias2 as a2; +use crate::test::exports::the_test; + +#[derive(Clone)] +pub struct Component; + +impl a1::Handler for Component { + async fn a( + &self, + _cx: Ctx, + f: a1::Foo, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + Vec<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn>, + > { + Ok(vec![f.thing]) + } +} + +impl a1::HandlerThing for Component { + async fn new( + &self, + _cx: Ctx, + s: String, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn, + > { + let contents = format!("{s} GuestThing"); + Ok(::wit_bindgen_wrpc::wrpc_transport::ResourceOwn::from( + ::wit_bindgen_wrpc::bytes::Bytes::copy_from_slice(contents.as_bytes()), + )) + } + + async fn get( + &self, + _cx: Ctx, + self_: ::wit_bindgen_wrpc::wrpc_transport::ResourceBorrow, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + let repr: &[u8] = self_.as_ref(); + let contents = String::from_utf8(repr.to_vec()).unwrap(); + Ok(format!("{contents} GuestThing.get")) + } +} + +impl a2::Handler for Component { + async fn b( + &self, + _cx: Ctx, + f: a2::Foo, + g: a2::Bar, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + Vec<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn>, + > { + Ok(vec![f.thing, g.thing]) + } +} + +impl the_test::Handler for Component { + async fn test( + &self, + _cx: Ctx, + things: Vec<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn>, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + Vec<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn>, + > { + Ok(things) + } +} diff --git a/tests/runtime/resource_alias_redux/test.wit b/tests/runtime/resource_alias_redux/test.wit new file mode 100644 index 000000000..9a3054bd6 --- /dev/null +++ b/tests/runtime/resource_alias_redux/test.wit @@ -0,0 +1,42 @@ +package test:resource-alias-redux; + +interface resource-alias1 { + resource thing { + constructor(s: string); + get: func() -> string; + } + + record foo { thing: thing } + + a: func(f: foo) -> list; +} + +interface resource-alias2 { + use resource-alias1.{thing, foo as bar}; + + record foo { thing: thing } + + b: func(f: foo, g: bar) -> list; +} + +world test { + export resource-alias1; + export resource-alias2; + + export the-test: interface { + use resource-alias1.{thing}; + test: func(things: list) -> list; + } +} + +world runner { + import resource-alias1; + import resource-alias2; + + import the-test: interface { + use resource-alias1.{thing}; + test: func(things: list) -> list; + } + + export run: func(); +} diff --git a/tests/runtime/resource_borrow_in_record/runner.rs b/tests/runtime/resource_borrow_in_record/runner.rs new file mode 100644 index 000000000..498ab8694 --- /dev/null +++ b/tests/runtime/resource_borrow_in_record/runner.rs @@ -0,0 +1,27 @@ +use crate::runner::test::resource_borrow_in_record::to_test::{test, Foo, Thing}; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let thing1 = Thing::new(wrpc, (), "Bonjour").await?; + let thing2 = Thing::new(wrpc, (), "mon cher").await?; + let things = test( + wrpc, + (), + &[ + Foo { + thing: thing1.as_borrow(), + }, + Foo { + thing: thing2.as_borrow(), + }, + ], + ) + .await?; + let mut result = Vec::new(); + for thing in &things { + result.push(Thing::get(wrpc, (), &thing.as_borrow()).await?); + } + assert_eq!(result, ["Bonjour new test get", "mon cher new test get"]); + Ok(()) +} diff --git a/tests/runtime/resource_borrow_in_record/test.rs b/tests/runtime/resource_borrow_in_record/test.rs new file mode 100644 index 000000000..82c07a5d8 --- /dev/null +++ b/tests/runtime/resource_borrow_in_record/test.rs @@ -0,0 +1,55 @@ +use crate::test::exports::test::resource_borrow_in_record::to_test::{ + Foo, Handler, HandlerThing, Thing, +}; + +#[derive(Clone)] +pub struct Component; + +fn contents_of(repr: &[u8]) -> String { + String::from_utf8(repr.to_vec()).unwrap() +} + +fn handle_from(contents: &str) -> ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn { + ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn::from( + ::wit_bindgen_wrpc::bytes::Bytes::copy_from_slice(contents.as_bytes()), + ) +} + +impl Handler for Component { + async fn test( + &self, + _cx: Ctx, + a: Vec, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + Vec<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn>, + > { + Ok(a + .iter() + .map(|foo| { + let contents = contents_of(foo.thing.as_ref()); + handle_from(&format!("{contents} test")) + }) + .collect()) + } +} + +impl HandlerThing for Component { + async fn new( + &self, + _cx: Ctx, + s: String, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn, + > { + Ok(handle_from(&format!("{s} new"))) + } + + async fn get( + &self, + _cx: Ctx, + self_: ::wit_bindgen_wrpc::wrpc_transport::ResourceBorrow, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + let contents = contents_of(self_.as_ref()); + Ok(format!("{contents} get")) + } +} diff --git a/tests/runtime/resource_borrow_in_record/test.wit b/tests/runtime/resource_borrow_in_record/test.wit new file mode 100644 index 000000000..ac9bfdbe2 --- /dev/null +++ b/tests/runtime/resource_borrow_in_record/test.wit @@ -0,0 +1,24 @@ +package test:resource-borrow-in-record; + +interface to-test { + resource thing { + constructor(s: string); + get: func() -> string; + } + + record foo { + thing: borrow + } + + test: func(a: list) -> list; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/resource_borrow_in_record/wasm.rs b/tests/runtime/resource_borrow_in_record/wasm.rs new file mode 100644 index 000000000..3f53c53c8 --- /dev/null +++ b/tests/runtime/resource_borrow_in_record/wasm.rs @@ -0,0 +1,54 @@ +wit_bindgen::generate!({ + path: "../../tests/runtime/resource_borrow_in_record", +}); + +use exports::test::resource_borrow_in_record::test::{Guest, GuestThing, Thing as ThingExport}; +use test::resource_borrow_in_record::test::Foo; +use test::resource_borrow_in_record::test::Thing; + +pub struct Test {} + +export!(Test); + +impl Guest for Test { + type Thing = MyThing; + + fn test( + a: Vec, + ) -> Vec { + let foo = a + .iter() + .map( + |a: &exports::test::resource_borrow_in_record::test::Foo| Foo { + thing: &a.thing.get::().thing, + }, + ) + .collect::>(); + test::resource_borrow_in_record::test::test(&foo) + .into_iter() + .map(|a| ThingExport::new(MyThing::from_thing(a))) + .collect() + } +} + +#[derive(Debug)] +pub struct MyThing { + thing: Thing, +} + +impl MyThing { + pub fn from_thing(thing: Thing) -> Self { + Self { thing } + } +} + +impl GuestThing for MyThing { + fn new(s: String) -> Self { + Self { + thing: Thing::new(&format!("{} Thing", s)), + } + } + fn get(&self) -> String { + self.thing.get() + " Thing.get" + } +} diff --git a/tests/runtime/rust/alternative-bitflags/runner.rs b/tests/runtime/rust/alternative-bitflags/runner.rs index 6bc7c8999..0b7ef9882 100644 --- a/tests/runtime/rust/alternative-bitflags/runner.rs +++ b/tests/runtime/rust/alternative-bitflags/runner.rs @@ -1,7 +1,12 @@ +//@ args = '--bitflags-path crate::runner::my_bitflags' + +pub(crate) use ::wit_bindgen_wrpc::bitflags as my_bitflags; + +use crate::runner::my::inline::t::{get_flag, Bar}; + pub async fn run( - clt: &impl wit_bindgen_wrpc::wrpc_transport::Invoke, -) -> anyhow::Result<()> { - let flag = my::inline::flags_iface::get_flag(clt, ()).await?; - assert_eq!(flag, my::inline::flags_iface::Bar::BAZ); + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(get_flag(wrpc, ()).await?, Bar::BAZ); Ok(()) } diff --git a/tests/runtime/rust/alternative-bitflags/test.rs b/tests/runtime/rust/alternative-bitflags/test.rs index 851ef1e96..f82268e88 100644 --- a/tests/runtime/rust/alternative-bitflags/test.rs +++ b/tests/runtime/rust/alternative-bitflags/test.rs @@ -1,12 +1,14 @@ -//@ args = '--bitflags-path=wit_bindgen_wrpc::bitflags' +//@ args = '--bitflags-path crate::test::my_bitflags' -use exports::my::inline::flags_iface::Bar; +pub(crate) use ::wit_bindgen_wrpc::bitflags as my_bitflags; + +use crate::test::exports::my::inline::t::{Bar, Handler}; #[derive(Clone)] pub struct Component; -impl exports::my::inline::flags_iface::Handler for Component { - async fn get_flag(&self, _cx: Ctx) -> anyhow::Result { +impl Handler for Component { + async fn get_flag(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { Ok(Bar::BAZ) } } diff --git a/tests/runtime/rust/alternative-bitflags/test.wit b/tests/runtime/rust/alternative-bitflags/test.wit index b06c9888b..3e59c6342 100644 --- a/tests/runtime/rust/alternative-bitflags/test.wit +++ b/tests/runtime/rust/alternative-bitflags/test.wit @@ -1,18 +1,21 @@ package my:inline; -interface flags-iface { - flags bar { - foo, - bar, - baz, - } - get-flag: func() -> bar; +interface t { + flags bar { + foo, + bar, + baz + } + + get-flag: func() -> bar; } world test { - export flags-iface; + export t; } world runner { - import flags-iface; + import t; + + export run: func(); } diff --git a/tests/runtime/rust/async-modifier/runner.rs b/tests/runtime/rust/async-modifier/runner.rs deleted file mode 100644 index 5ad6074d7..000000000 --- a/tests/runtime/rust/async-modifier/runner.rs +++ /dev/null @@ -1,11 +0,0 @@ -use my::inline::i::{f, one_argument, one_argument_and_result, one_result}; - -pub async fn run( - clt: &impl wit_bindgen_wrpc::wrpc_transport::Invoke, -) -> anyhow::Result<()> { - f(clt, ()).await?; - one_argument(clt, (), 1).await?; - assert_eq!(one_result(clt, ()).await?, 2); - assert_eq!(one_argument_and_result(clt, (), 3).await?, 4); - Ok(()) -} diff --git a/tests/runtime/rust/async-modifier/test.rs b/tests/runtime/rust/async-modifier/test.rs deleted file mode 100644 index 92e1ee3e9..000000000 --- a/tests/runtime/rust/async-modifier/test.rs +++ /dev/null @@ -1,19 +0,0 @@ -#[derive(Clone)] -pub struct Component; - -impl exports::my::inline::i::Handler for Component { - async fn f(&self, _cx: Ctx) -> anyhow::Result<()> { - Ok(()) - } - async fn one_argument(&self, _cx: Ctx, x: u32) -> anyhow::Result<()> { - assert_eq!(x, 1); - Ok(()) - } - async fn one_result(&self, _cx: Ctx) -> anyhow::Result { - Ok(2) - } - async fn one_argument_and_result(&self, _cx: Ctx, x: u32) -> anyhow::Result { - assert_eq!(x, 3); - Ok(4) - } -} diff --git a/tests/runtime/rust/async-modifier/test.wit b/tests/runtime/rust/async-modifier/test.wit deleted file mode 100644 index 48f034e94..000000000 --- a/tests/runtime/rust/async-modifier/test.wit +++ /dev/null @@ -1,11 +0,0 @@ -package my:inline; - -interface i { - f: async func(); - one-argument: async func(x: u32); - one-result: async func() -> u32; - one-argument-and-result: async func(x: u32) -> u32; -} - -world test { export i; } -world runner { import i; } diff --git a/tests/runtime/rust/custom-derives/runner.rs b/tests/runtime/rust/custom-derives/runner.rs index d2d70a374..16cbd30ae 100644 --- a/tests/runtime/rust/custom-derives/runner.rs +++ b/tests/runtime/rust/custom-derives/runner.rs @@ -1,10 +1,10 @@ -use my::inline::blah::{bar, Foo}; +use crate::runner::my::inline::blah::{bar, Foo}; pub async fn run( - clt: &impl wit_bindgen_wrpc::wrpc_transport::Invoke, -) -> anyhow::Result<()> { + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { bar( - clt, + wrpc, (), &Foo { field1: "x".to_string(), diff --git a/tests/runtime/rust/custom-derives/test.rs b/tests/runtime/rust/custom-derives/test.rs index 34716fff2..ae3111ee9 100644 --- a/tests/runtime/rust/custom-derives/test.rs +++ b/tests/runtime/rust/custom-derives/test.rs @@ -1,44 +1,46 @@ //@ args = [ //@ '-dHash', //@ '-dClone', -//@ '-d::core::cmp::PartialEq', -//@ '-d::core::cmp::Eq', -//@ '-dserde::Serialize', -//@ '-dserde::Deserialize', +//@ '-dstd::cmp::PartialEq', +//@ '-dcore::cmp::Eq', //@ '--additional-derive-ignore=ignoreme', //@ ] +use crate::test::exports::my::inline::blag::{Handler as HandlerBlag, HandlerInputStream}; +use crate::test::exports::my::inline::blah::{Foo, Handler as HandlerBlah, Ignoreme}; use std::collections::{hash_map::RandomState, HashSet}; -use exports::my::inline::blah::Foo; - #[derive(Clone)] pub struct Component; -impl exports::my::inline::blah::Handler for Component { - async fn bar(&self, _cx: Ctx, cool: Foo) -> anyhow::Result<()> { - // The added `Hash`/`Eq` derives must apply to `foo`, so it can be used - // as a `HashSet` element. - let _blah: HashSet = HashSet::from_iter([Foo { - field1: "hello".to_string(), - field2: vec![1, 2, 3], - }]); +impl HandlerBlah for Component { + async fn bar(&self, _cx: Ctx, cool: Foo) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let _blah: HashSet = HashSet::from_iter([ + Foo { + field1: "hello".to_string(), + field2: vec![1, 2, 3], + }, + cool, + ]); + Ok(()) + } - // The added `serde` derives must apply too, otherwise this fails to - // compile. - let _ = serde_json::to_string(&cool); + async fn barry(&self, _cx: Ctx, _warm: Ignoreme) -> ::wit_bindgen_wrpc::anyhow::Result<()> { Ok(()) } +} + +impl HandlerBlag for Component {} - async fn barry( +impl HandlerInputStream for Component { + async fn read( &self, _cx: Ctx, - warm: exports::my::inline::blah::Ignoreme, - ) -> anyhow::Result<()> { - // Compilation would fail here if `serde::Deserialize` were applied to - // `ignoreme`, since it holds a resource handle. `--additional-derive-ignore` - // must have excluded it. - let _ = warm; - Ok(()) + _stream: ::wit_bindgen_wrpc::wrpc_transport::ResourceBorrow< + crate::test::exports::my::inline::blag::InputStream, + >, + _len: u64, + ) -> ::wit_bindgen_wrpc::anyhow::Result<::wit_bindgen_wrpc::bytes::Bytes> { + todo!() } } diff --git a/tests/runtime/rust/custom-derives/test.wit b/tests/runtime/rust/custom-derives/test.wit index d3b493db8..f05d7042e 100644 --- a/tests/runtime/rust/custom-derives/test.wit +++ b/tests/runtime/rust/custom-derives/test.wit @@ -7,26 +7,27 @@ interface blag { } interface blah { - use blag.{input-stream}; + use blag.{input-stream}; + record foo { + field1: string, + field2: list + } - record foo { - field1: string, - field2: list, - } - - bar: func(cool: foo); + bar: func(cool: foo); - variant ignoreme { - stream-type(input-stream), - } + variant ignoreme { + stream-type(input-stream), + } - barry: func(warm: ignoreme); + barry: func(warm: ignoreme); } world test { - export blah; + export blag; + export blah; } - world runner { - import blah; + import blah; + + export run: func(); } diff --git a/tests/runtime/rust/gated-features/runner.rs b/tests/runtime/rust/gated-features/runner.rs deleted file mode 100644 index ea41f4ccc..000000000 --- a/tests/runtime/rust/gated-features/runner.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ args = '--features y' - -pub async fn run( - clt: &impl wit_bindgen_wrpc::wrpc_transport::Invoke, -) -> anyhow::Result<()> { - foo::bar::iface::y(clt, ()).await?; - foo::bar::iface::z(clt, ()).await?; - Ok(()) -} diff --git a/tests/runtime/rust/gated-features/test.rs b/tests/runtime/rust/gated-features/test.rs deleted file mode 100644 index 9475139d9..000000000 --- a/tests/runtime/rust/gated-features/test.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ args = '--features y' - -#[derive(Clone)] -pub struct Component; - -impl exports::foo::bar::iface::Handler for Component { - async fn y(&self, _cx: Ctx) -> anyhow::Result<()> { - Ok(()) - } - async fn z(&self, _cx: Ctx) -> anyhow::Result<()> { - Ok(()) - } -} diff --git a/tests/runtime/rust/gated-features/test.wit b/tests/runtime/rust/gated-features/test.wit deleted file mode 100644 index a4dc06a56..000000000 --- a/tests/runtime/rust/gated-features/test.wit +++ /dev/null @@ -1,18 +0,0 @@ -package foo:bar@1.2.3; - -interface iface { - @unstable(feature = x) - x: func(); - @unstable(feature = y) - y: func(); - @since(version = 1.2.3) - z: func(); -} - -world test { - export iface; -} - -world runner { - import iface; -} diff --git a/tests/runtime/rust/owned-resource-deref-mut/runner.rs b/tests/runtime/rust/owned-resource-deref-mut/runner.rs new file mode 100644 index 000000000..c540cbd65 --- /dev/null +++ b/tests/runtime/rust/owned-resource-deref-mut/runner.rs @@ -0,0 +1,10 @@ +use crate::runner::my::inline::foo::Bar; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let data = Bar::new(wrpc, (), 3).await?; + assert_eq!(Bar::get_data(wrpc, (), &data.as_borrow()).await?, 3); + assert_eq!(Bar::consume(wrpc, (), &data).await?, 4); + Ok(()) +} diff --git a/tests/runtime/rust/owned-resource-deref-mut/test.rs b/tests/runtime/rust/owned-resource-deref-mut/test.rs new file mode 100644 index 000000000..2aaaf5bf6 --- /dev/null +++ b/tests/runtime/rust/owned-resource-deref-mut/test.rs @@ -0,0 +1,39 @@ +use crate::test::exports::my::inline::foo::{Handler, HandlerBar, Bar}; + +#[derive(Clone)] +pub struct Component; + +impl Handler for Component {} + +impl HandlerBar for Component { + async fn new( + &self, + _cx: Ctx, + data: u32, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn, + > { + Ok(::wit_bindgen_wrpc::wrpc_transport::ResourceOwn::from( + ::wit_bindgen_wrpc::bytes::Bytes::copy_from_slice(&data.to_le_bytes()), + )) + } + + async fn get_data( + &self, + _cx: Ctx, + self_: ::wit_bindgen_wrpc::wrpc_transport::ResourceBorrow, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + let bytes: &[u8] = self_.as_ref(); + Ok(u32::from_le_bytes(bytes.try_into().unwrap())) + } + + async fn consume( + &self, + _cx: Ctx, + self_: ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + let bytes: &[u8] = self_.as_ref(); + let data = u32::from_le_bytes(bytes.try_into().unwrap()); + Ok(data + 1) + } +} diff --git a/tests/runtime/rust/owned-resource-deref-mut/test.wit b/tests/runtime/rust/owned-resource-deref-mut/test.wit new file mode 100644 index 000000000..133a25236 --- /dev/null +++ b/tests/runtime/rust/owned-resource-deref-mut/test.wit @@ -0,0 +1,18 @@ +package my:inline; + +interface foo { + resource bar { + constructor(data: u32); + get-data: func() -> u32; + consume: static func(%self: bar) -> u32; + } +} + +world test { + export foo; +} +world runner { + import foo; + + export run: func(); +} diff --git a/tests/runtime/rust/resource_into_inner/runner.rs b/tests/runtime/rust/resource_into_inner/runner.rs new file mode 100644 index 000000000..17d29f424 --- /dev/null +++ b/tests/runtime/rust/resource_into_inner/runner.rs @@ -0,0 +1,8 @@ +use crate::runner::test::resource_into_inner::to_test::test; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + test(wrpc, ()).await?; + Ok(()) +} diff --git a/tests/runtime/rust/resource_into_inner/test.rs b/tests/runtime/rust/resource_into_inner/test.rs new file mode 100644 index 000000000..2d6e79f52 --- /dev/null +++ b/tests/runtime/rust/resource_into_inner/test.rs @@ -0,0 +1,24 @@ +use crate::test::exports::test::resource_into_inner::to_test::{Handler, HandlerThing, Thing}; + +#[derive(Clone)] +pub struct Component; + +impl Handler for Component { + async fn test(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + Ok(()) + } +} + +impl HandlerThing for Component { + async fn new( + &self, + _cx: Ctx, + text: String, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn, + > { + Ok(::wit_bindgen_wrpc::wrpc_transport::ResourceOwn::from( + ::wit_bindgen_wrpc::bytes::Bytes::copy_from_slice(text.as_bytes()), + )) + } +} diff --git a/tests/runtime/rust/resource_into_inner/test.wit b/tests/runtime/rust/resource_into_inner/test.wit new file mode 100644 index 000000000..694db22cb --- /dev/null +++ b/tests/runtime/rust/resource_into_inner/test.wit @@ -0,0 +1,19 @@ +package test:resource-into-inner; + +interface to-test { + resource thing { + constructor(text: string); + } + + test: func(); +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/rust/skip/runner.rs b/tests/runtime/rust/skip/runner.rs index 5a855280d..e7cf550ca 100644 --- a/tests/runtime/rust/skip/runner.rs +++ b/tests/runtime/rust/skip/runner.rs @@ -1,6 +1,8 @@ +use crate::runner::exports::bar; + pub async fn run( - clt: &impl wit_bindgen_wrpc::wrpc_transport::Invoke, -) -> anyhow::Result<()> { - my::test::exports_iface::bar(clt, ()).await?; + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + bar(wrpc, ()).await?; Ok(()) } diff --git a/tests/runtime/rust/skip/test.rs b/tests/runtime/rust/skip/test.rs index 3137e2cef..d32ac4eac 100644 --- a/tests/runtime/rust/skip/test.rs +++ b/tests/runtime/rust/skip/test.rs @@ -1,10 +1,12 @@ //@ args = '--skip foo' +use crate::test::exports::exports::Handler; + #[derive(Clone)] pub struct Component; -impl exports::my::test::exports_iface::Handler for Component { - async fn bar(&self, _cx: Ctx) -> anyhow::Result<()> { +impl Handler for Component { + async fn bar(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { Ok(()) } } diff --git a/tests/runtime/rust/skip/test.wit b/tests/runtime/rust/skip/test.wit index 03c3fa8d0..d84bc1cfc 100644 --- a/tests/runtime/rust/skip/test.wit +++ b/tests/runtime/rust/skip/test.wit @@ -1,14 +1,17 @@ package my:test; -interface exports-iface { - foo: func(); - bar: func(); -} +world runner { + import exports: interface { + foo: func(); + bar: func(); + } -world test { - export exports-iface; + export run: func(); } -world runner { - import exports-iface; +world test { + export exports: interface { + foo: func(); + bar: func(); + } } diff --git a/tests/runtime/rust/type_section_suffix/runner.rs b/tests/runtime/rust/type_section_suffix/runner.rs new file mode 100644 index 000000000..b928a3754 --- /dev/null +++ b/tests/runtime/rust/type_section_suffix/runner.rs @@ -0,0 +1,8 @@ +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + crate::runner::test::suffix::imports::foo(wrpc, ()).await?; + crate::runner::foo::f(wrpc, ()).await?; + crate::runner::bar::f(wrpc, ()).await?; + Ok(()) +} diff --git a/tests/runtime/rust/type_section_suffix/test.rs b/tests/runtime/rust/type_section_suffix/test.rs new file mode 100644 index 000000000..0352f5282 --- /dev/null +++ b/tests/runtime/rust/type_section_suffix/test.rs @@ -0,0 +1,20 @@ +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::bar::Handler for Component { + async fn f(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + Ok(()) + } +} + +impl crate::test::exports::foo::Handler for Component { + async fn f(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + Ok(()) + } +} + +impl crate::test::exports::test::suffix::imports::Handler for Component { + async fn foo(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + Ok(()) + } +} diff --git a/tests/runtime/rust/type_section_suffix/test.wit b/tests/runtime/rust/type_section_suffix/test.wit new file mode 100644 index 000000000..d50009d3b --- /dev/null +++ b/tests/runtime/rust/type_section_suffix/test.wit @@ -0,0 +1,39 @@ +package test:suffix; + +interface imports { + foo: func(); +} + +world available-imports { + import imports; + include test:a/imports; + include test:b/imports; +} + +world runner { + include available-imports; + + export run: func(); +} + +world test { + export imports; + export foo: interface { f: func(); } + export bar: interface { f: func(); } +} + +package test:a { + world imports { + import foo: interface { + f: func(); + } + } +} + +package test:b { + world imports { + import bar: interface { + f: func(); + } + } +} diff --git a/tests/runtime/rust/unused-types/runner.rs b/tests/runtime/rust/unused-types/runner.rs deleted file mode 100644 index bc8fa0a26..000000000 --- a/tests/runtime/rust/unused-types/runner.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub async fn run( - clt: &impl wit_bindgen_wrpc::wrpc_transport::Invoke, -) -> anyhow::Result<()> { - foo::bar::component::ping(clt, ()).await?; - Ok(()) -} diff --git a/tests/runtime/rust/unused-types/test.rs b/tests/runtime/rust/unused-types/test.rs deleted file mode 100644 index a3ee062fd..000000000 --- a/tests/runtime/rust/unused-types/test.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ args = '--generate-unused-types' - -#[derive(Clone)] -pub struct Component; - -impl exports::foo::bar::component::Handler for Component { - async fn ping(&self, _cx: Ctx) -> anyhow::Result<()> { - Ok(()) - } -} diff --git a/tests/runtime/rust/unused-types/test.wit b/tests/runtime/rust/unused-types/test.wit deleted file mode 100644 index da10a8a31..000000000 --- a/tests/runtime/rust/unused-types/test.wit +++ /dev/null @@ -1,23 +0,0 @@ -package foo:bar; - -interface component { - variant unused-variant { - %enum(unused-enum), - %record(unused-record), - } - enum unused-enum { - unused, - } - record unused-record { - x: u32, - } - ping: func(); -} - -world test { - export component; -} - -world runner { - import component; -} diff --git a/tests/runtime/rust/with-and-resources/runner.rs b/tests/runtime/rust/with-and-resources/runner.rs new file mode 100644 index 000000000..742eb90ac --- /dev/null +++ b/tests/runtime/rust/with-and-resources/runner.rs @@ -0,0 +1,28 @@ +//@ args = '--with my:inline/foo=other::my::inline::foo' + +mod other { + ::wit_bindgen_wrpc::generate!({ + inline: " + package my:inline; + + interface foo { + resource a; + + bar: func() -> a; + } + + world dummy { + import foo; + } + ", + generate_all, + }); +} + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let resource = other::my::inline::foo::bar(wrpc, ()).await?; + let _ = crate::runner::my::inline::bar::bar(wrpc, (), &resource).await?; + Ok(()) +} diff --git a/tests/runtime/rust/with-and-resources/test.rs b/tests/runtime/rust/with-and-resources/test.rs new file mode 100644 index 000000000..500fd460c --- /dev/null +++ b/tests/runtime/rust/with-and-resources/test.rs @@ -0,0 +1,30 @@ +use crate::test::exports::my::inline::bar::Handler as HandlerBar; +use crate::test::exports::my::inline::foo::{Handler as HandlerFoo, HandlerA, A}; + +#[derive(Clone)] +pub struct Component; + +impl HandlerFoo for Component { + async fn bar( + &self, + _cx: Ctx, + ) -> ::wit_bindgen_wrpc::anyhow::Result<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn> { + Ok(::wit_bindgen_wrpc::wrpc_transport::ResourceOwn::from( + ::wit_bindgen_wrpc::bytes::Bytes::from_static(b"a"), + )) + } +} + +impl HandlerA for Component {} + +impl HandlerBar for Component { + async fn bar( + &self, + _cx: Ctx, + m: ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn, + ) -> ::wit_bindgen_wrpc::anyhow::Result< + Vec<::wit_bindgen_wrpc::wrpc_transport::ResourceOwn>, + > { + Ok(vec![m]) + } +} diff --git a/tests/runtime/rust/with-and-resources/test.wit b/tests/runtime/rust/with-and-resources/test.wit new file mode 100644 index 000000000..a2369441d --- /dev/null +++ b/tests/runtime/rust/with-and-resources/test.wit @@ -0,0 +1,24 @@ +package my:inline; + +interface foo { + resource a; + + bar: func() -> a; +} + +interface bar { + use foo.{a}; + + bar: func(m: a) -> list; +} + +world test { + export foo; + export bar; +} + +world runner { + import bar; + + export run: func(); +} diff --git a/tests/runtime/rust/with-option-generate/runner-generate-all.rs b/tests/runtime/rust/with-option-generate/runner-generate-all.rs index 2bf48c033..a601653be 100644 --- a/tests/runtime/rust/with-option-generate/runner-generate-all.rs +++ b/tests/runtime/rust/with-option-generate/runner-generate-all.rs @@ -1,8 +1,8 @@ //@ args = '--generate-all' pub async fn run( - clt: &impl wit_bindgen_wrpc::wrpc_transport::Invoke, -) -> anyhow::Result<()> { - foo::baz::a::x(clt, ()).await?; + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + crate::runner::foo::baz::a::x(wrpc, ()).await?; Ok(()) } diff --git a/tests/runtime/rust/with-option-generate/runner-generate-one.rs b/tests/runtime/rust/with-option-generate/runner-generate-one.rs index cd0140df7..2acf3a868 100644 --- a/tests/runtime/rust/with-option-generate/runner-generate-one.rs +++ b/tests/runtime/rust/with-option-generate/runner-generate-one.rs @@ -1,8 +1,8 @@ -//@ args = '--with=foo:baz/a=generate' +//@ args = '--with foo:baz/a=generate' pub async fn run( - clt: &impl wit_bindgen_wrpc::wrpc_transport::Invoke, -) -> anyhow::Result<()> { - foo::baz::a::x(clt, ()).await?; + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + crate::runner::foo::baz::a::x(wrpc, ()).await?; Ok(()) } diff --git a/tests/runtime/rust/with-option-generate/test.rs b/tests/runtime/rust/with-option-generate/test.rs index d4fcd82c7..8284fd43c 100644 --- a/tests/runtime/rust/with-option-generate/test.rs +++ b/tests/runtime/rust/with-option-generate/test.rs @@ -3,8 +3,8 @@ #[derive(Clone)] pub struct Component; -impl exports::foo::baz::a::Handler for Component { - async fn x(&self, _cx: Ctx) -> anyhow::Result<()> { +impl crate::test::exports::foo::baz::a::Handler for Component { + async fn x(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { Ok(()) } } diff --git a/tests/runtime/rust/with-option-generate/test.wit b/tests/runtime/rust/with-option-generate/test.wit index 74653a7af..a972ea784 100644 --- a/tests/runtime/rust/with-option-generate/test.wit +++ b/tests/runtime/rust/with-option-generate/test.wit @@ -1,17 +1,21 @@ //@ default-bindgen-args = false +// Note that default bindgen args, where Rust uses `--generate-all`, is +// specifically disabled for this test as that's what's being tested here. + package foo:bar; world test { - export foo:baz/a; + export foo:baz/a; } - world runner { - import foo:baz/a; + import foo:baz/a; + + export run: func(); } package foo:baz { interface a { - x: func(); + x: func(); } } diff --git a/tests/runtime/rust/with-types/runner.rs b/tests/runtime/rust/with-types/runner.rs index 4e1057662..16e58547b 100644 --- a/tests/runtime/rust/with-types/runner.rs +++ b/tests/runtime/rust/with-types/runner.rs @@ -1,32 +1,80 @@ //@ args = [ -//@ '--with=my:inline/foo/a=crate::runner::other::my::inline::foo::A', -//@ '--with=my:inline/foo/c=crate::runner::other::my::inline::foo::C', +//@ '--with=my:inline/foo/a=crate::runner::my_types::MyA', +//@ '--with=my:inline/foo/b=crate::runner::my_types::MyB', +//@ '--with=my:inline/foo/c=crate::runner::my_types::MyC', +//@ '--with=d=crate::runner::my_types::MyD', +//@ '--with=my:inline/bar/e=crate::runner::my_types::MyE', +//@ '--with=my:inline/foo/f=generate', //@ ] -mod other { - wit_bindgen_wrpc::generate!({ +mod my_types { + ::wit_bindgen_wrpc::generate!({ inline: " - package my:inline; - interface foo { - record a { inner: f64, } - variant c { a(a), other(u32), } + package my:types; + + interface t { + record a { + inner: f64, + } + + resource b; + + variant c { + a(a), + b(b), + } + + record d { + inner: u32, + } + + record e { + inner: u32, + } + + use-a: func(v: a) -> a; + use-b: func(v: b) -> b; + use-c: func(v: c) -> c; + use-d: func(v: d) -> d; + use-e: func(v: e) -> e; } + world dummy { - use foo.{a, c}; - import f: func(v: a, w: c); + import t; } ", + generate_all, }); + + pub use self::my::types::t::{A as MyA, B as MyB, C as MyC, D as MyD, E as MyE}; } pub async fn run( - clt: &impl wit_bindgen_wrpc::wrpc_transport::Invoke, -) -> anyhow::Result<()> { - let a = other::my::inline::foo::A { inner: 1.5 }; - let got = my::inline::foo::func1(clt, (), &a).await?; - assert_eq!(got.inner, 1.5); - - let c = other::my::inline::foo::C::A(a); - my::inline::foo::func3(clt, (), &c).await?; + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + let a = my_types::MyA { inner: 0.0 }; + let _ = crate::runner::my::inline::foo::func1(wrpc, (), &a).await?; + + // can't actually succeed at runtime as this is faking a resource, so check + // that it compiles but dynamically skip it. + if false { + let b = ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn::::from( + ::wit_bindgen_wrpc::bytes::Bytes::new(), + ); + let _ = crate::runner::my::inline::foo::func2(wrpc, (), &b).await?; + } + + let c = my_types::MyC::A(a); + let _ = crate::runner::i::func7(wrpc, (), &c).await?; + + let a_list = vec![a, a]; + let _ = crate::runner::my::inline::foo::func3(wrpc, (), &a_list).await?; + + let _ = crate::runner::my::inline::foo::func4(wrpc, (), Some(a)).await?; + + let _ = crate::runner::my::inline::foo::func5(wrpc, ()).await?; + + let d = my_types::MyD { inner: 0 }; + let _ = crate::runner::i::func8(wrpc, (), &d).await?; Ok(()) } diff --git a/tests/runtime/rust/with-types/test.rs b/tests/runtime/rust/with-types/test.rs index 9c99f2dea..cb88ab4ef 100644 --- a/tests/runtime/rust/with-types/test.rs +++ b/tests/runtime/rust/with-types/test.rs @@ -1,13 +1,54 @@ -use exports::my::inline::foo::{A, C}; +//@ args = '--generate-all' + +use ::wit_bindgen_wrpc::anyhow::Result; +use ::wit_bindgen_wrpc::wrpc_transport::ResourceOwn; + +use crate::test::exports::i::{C as IC, D as ID, Handler as HandlerI}; +use crate::test::exports::my::inline::bar::{E, Handler as HandlerBar}; +use crate::test::exports::my::inline::foo::{ + A, B, F, G, Handler as HandlerFoo, HandlerB, +}; #[derive(Clone)] pub struct Component; -impl exports::my::inline::foo::Handler for Component { - async fn func1(&self, _cx: Ctx, v: A) -> anyhow::Result { +impl HandlerFoo for Component { + async fn func1(&self, _cx: Ctx, v: A) -> Result { + Ok(v) + } + async fn func2(&self, _cx: Ctx, v: ResourceOwn) -> Result> { + Ok(v) + } + async fn func3(&self, _cx: Ctx, _v: Vec) -> Result>> { + Ok(Vec::new()) + } + async fn func4(&self, _cx: Ctx, v: Option) -> Result> { + Ok(v) + } + async fn func5(&self, _cx: Ctx) -> Result> { + Ok(Err(())) + } + async fn func6(&self, _cx: Ctx) -> Result> { + Ok(Err(())) + } + async fn func7(&self, _cx: Ctx) -> Result> { + Ok(Err(())) + } +} + +impl HandlerB for Component {} + +impl HandlerBar for Component { + async fn func6(&self, _cx: Ctx, v: E) -> Result { + Ok(v) + } +} + +impl HandlerI for Component { + async fn func7(&self, _cx: Ctx, v: IC) -> Result { Ok(v) } - async fn func3(&self, _cx: Ctx, v: C) -> anyhow::Result { + async fn func8(&self, _cx: Ctx, v: ID) -> Result { Ok(v) } } diff --git a/tests/runtime/rust/with-types/test.wit b/tests/runtime/rust/with-types/test.wit index 2a6e6e228..cafd58a1e 100644 --- a/tests/runtime/rust/with-types/test.wit +++ b/tests/runtime/rust/with-types/test.wit @@ -1,21 +1,78 @@ +//@ default-bindgen-args = false + package my:inline; interface foo { - record a { - inner: f64, - } - variant c { - a(a), - other(u32), - } - func1: func(v: a) -> a; - func3: func(v: c) -> c; + + record a { + inner: f64, + } + + resource b; + + variant c { + a(a), + b(b), + } + + // test type definition generation with `generate` option + record f { + inner: u32, + } + + // test type definition generation without `generate` option + record g { + inner: u32, + } + + func1: func(v: a) -> a; + func2: func(v: b) -> b; + func3: func(v: list) -> list; + func4: func(v: option) -> option; + func5: func() -> result; + func6: func() -> result; + func7: func() -> result; +} + +interface bar { + record e { + inner: u32, + } + + func6: func(v: e) -> e; } world test { - export foo; + export i: interface { + // test remapping with importing type directly + use foo.{c}; + func7: func(v: c) -> c; + + // test remapping the type defined in world + record d { + inner: u32, + } + + func8: func(v: d) -> d; + } + + export foo; + export bar; } world runner { - import foo; + import i: interface { + use foo.{c}; + func7: func(v: c) -> c; + + record d { + inner: u32, + } + + func8: func(v: d) -> d; + } + + import bar; + + export run: func(); } diff --git a/tests/runtime/rust/with/runner.rs b/tests/runtime/rust/with/runner.rs index 30cc8c376..e1c1162da 100644 --- a/tests/runtime/rust/with/runner.rs +++ b/tests/runtime/rust/with/runner.rs @@ -1,26 +1,31 @@ -//@ args = '--with=my:inline/foo=other::my::inline::foo' +//@ args = '--with my:inline/foo=other::my::inline::foo' mod other { - wit_bindgen_wrpc::generate!({ + ::wit_bindgen_wrpc::generate!({ inline: " package my:inline; + interface foo { - record msg { field: string, } + record msg { + field: string, + } } + world dummy { use foo.{msg}; import bar: func(m: msg); } ", + generate_all, }); } pub async fn run( - clt: &impl wit_bindgen_wrpc::wrpc_transport::Invoke, -) -> anyhow::Result<()> { + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { let msg = other::my::inline::foo::Msg { field: "hello".to_string(), }; - my::inline::bar::bar(clt, (), &msg).await?; + crate::runner::my::inline::bar::bar(wrpc, (), &msg).await?; Ok(()) } diff --git a/tests/runtime/rust/with/test.rs b/tests/runtime/rust/with/test.rs index 43011b397..74ccda38b 100644 --- a/tests/runtime/rust/with/test.rs +++ b/tests/runtime/rust/with/test.rs @@ -1,10 +1,12 @@ -use exports::my::inline::bar::Msg; - #[derive(Clone)] pub struct Component; -impl exports::my::inline::bar::Handler for Component { - async fn bar(&self, _cx: Ctx, m: Msg) -> anyhow::Result<()> { +impl crate::test::exports::my::inline::bar::Handler for Component { + async fn bar( + &self, + _cx: Ctx, + m: crate::test::exports::my::inline::bar::Msg, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { assert_eq!(m.field, "hello"); Ok(()) } diff --git a/tests/runtime/rust/with/test.wit b/tests/runtime/rust/with/test.wit index c70024504..a608e40cc 100644 --- a/tests/runtime/rust/with/test.wit +++ b/tests/runtime/rust/with/test.wit @@ -8,6 +8,7 @@ interface foo { interface bar { use foo.{msg}; + bar: func(m: msg); } @@ -17,4 +18,6 @@ world test { world runner { import bar; + + export run: func(); } diff --git a/tests/runtime/strings-alias/runner.rs b/tests/runtime/strings-alias/runner.rs new file mode 100644 index 000000000..95511eece --- /dev/null +++ b/tests/runtime/strings-alias/runner.rs @@ -0,0 +1,11 @@ +use crate::runner::cat::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + foo(wrpc, (), "hello").await?; + + let t: String = bar(wrpc, ()).await?; + assert_eq!(t, "world"); + Ok(()) +} diff --git a/tests/runtime/strings-alias/test.rs b/tests/runtime/strings-alias/test.rs new file mode 100644 index 000000000..49209a04c --- /dev/null +++ b/tests/runtime/strings-alias/test.rs @@ -0,0 +1,13 @@ +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::cat::Handler for Component { + async fn foo(&self, _cx: Ctx, x: String) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(x, "hello"); + Ok(()) + } + + async fn bar(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok("world".into()) + } +} diff --git a/tests/runtime/strings-alias/test.wit b/tests/runtime/strings-alias/test.wit new file mode 100644 index 000000000..a4fbc4926 --- /dev/null +++ b/tests/runtime/strings-alias/test.wit @@ -0,0 +1,19 @@ +package my:strings; + +world runner { + import cat: interface { + type my-string = string; + foo: func(x: my-string); + bar: func() -> my-string; + } + + export run: func(); +} + +world test { + export cat: interface { + type my-string = string; + foo: func(x: my-string); + bar: func() -> my-string; + } +} diff --git a/tests/runtime/strings-simple/runner.go b/tests/runtime/strings-simple/runner.go new file mode 100644 index 000000000..4cab3e710 --- /dev/null +++ b/tests/runtime/strings-simple/runner.go @@ -0,0 +1,24 @@ +package runner + +import ( + "context" + "fmt" + + wrpc "wrpc.io/go" + + "driver/runner/cat" +) + +func Run(ctx context.Context, c wrpc.Invoker) error { + if err := cat.Foo(ctx, c, "hello"); err != nil { + return err + } + value, err := cat.Bar(ctx, c) + if err != nil { + return err + } + if value != "world" { + panic(fmt.Sprintf("expected `world`; got `%v`", value)) + } + return nil +} diff --git a/tests/runtime/strings-simple/runner.rs b/tests/runtime/strings-simple/runner.rs new file mode 100644 index 000000000..95511eece --- /dev/null +++ b/tests/runtime/strings-simple/runner.rs @@ -0,0 +1,11 @@ +use crate::runner::cat::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + foo(wrpc, (), "hello").await?; + + let t: String = bar(wrpc, ()).await?; + assert_eq!(t, "world"); + Ok(()) +} diff --git a/tests/runtime/strings-simple/test.go b/tests/runtime/strings-simple/test.go new file mode 100644 index 000000000..ec4f9a895 --- /dev/null +++ b/tests/runtime/strings-simple/test.go @@ -0,0 +1,23 @@ +package test + +import ( + "context" + "fmt" +) + +type handler struct{} + +func NewHandler() handler { + return handler{} +} + +func (handler) Foo(ctx context.Context, x string) error { + if x != "hello" { + panic(fmt.Sprintf("unexpected value: `%v`", x)) + } + return nil +} + +func (handler) Bar(ctx context.Context) (string, error) { + return "world", nil +} diff --git a/tests/runtime/strings-simple/test.rs b/tests/runtime/strings-simple/test.rs new file mode 100644 index 000000000..49209a04c --- /dev/null +++ b/tests/runtime/strings-simple/test.rs @@ -0,0 +1,13 @@ +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::cat::Handler for Component { + async fn foo(&self, _cx: Ctx, x: String) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(x, "hello"); + Ok(()) + } + + async fn bar(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok("world".into()) + } +} diff --git a/tests/runtime/strings-simple/test.wit b/tests/runtime/strings-simple/test.wit new file mode 100644 index 000000000..df4f02d41 --- /dev/null +++ b/tests/runtime/strings-simple/test.wit @@ -0,0 +1,17 @@ +package my:strings; + +world runner { + import cat: interface { + foo: func(x: string); + bar: func() -> string; + } + + export run: func(); +} + +world test { + export cat: interface { + foo: func(x: string); + bar: func() -> string; + } +} diff --git a/tests/runtime/strings/runner.go b/tests/runtime/strings/runner.go new file mode 100644 index 000000000..871ea1d93 --- /dev/null +++ b/tests/runtime/strings/runner.go @@ -0,0 +1,37 @@ +package runner + +import ( + "context" + "fmt" + + wrpc "wrpc.io/go" + + "driver/runner/test/strings/to_test" +) + +func Run(ctx context.Context, c wrpc.Invoker) error { + must0(to_test.TakeBasic(ctx, c, "latin utf16")) + assertEqual(must(to_test.ReturnUnicode(ctx, c)), "🚀🚀🚀 𠈄𓀀") + assertEqual(must(to_test.ReturnEmpty(ctx, c)), "") + assertEqual(must(to_test.Roundtrip(ctx, c, "🚀🚀🚀 𠈄𓀀")), "🚀🚀🚀 𠈄𓀀") + return nil +} + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +func must0(err error) { + if err != nil { + panic(err) + } +} + +func assertEqual[T comparable](a T, b T) { + if a != b { + panic(fmt.Sprintf("%v not equal to %v", a, b)) + } +} diff --git a/tests/runtime/strings/runner.rs b/tests/runtime/strings/runner.rs new file mode 100644 index 000000000..a26333d9f --- /dev/null +++ b/tests/runtime/strings/runner.rs @@ -0,0 +1,11 @@ +use crate::runner::test::strings::to_test::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + take_basic(wrpc, (), "latin utf16").await?; + assert_eq!(return_unicode(wrpc, ()).await?, "🚀🚀🚀 𠈄𓀀"); + assert_eq!(return_empty(wrpc, ()).await?, ""); + assert_eq!(roundtrip(wrpc, (), "🚀🚀🚀 𠈄𓀀").await?, "🚀🚀🚀 𠈄𓀀"); + Ok(()) +} diff --git a/tests/runtime/strings/test.go b/tests/runtime/strings/test.go new file mode 100644 index 000000000..51eec4be0 --- /dev/null +++ b/tests/runtime/strings/test.go @@ -0,0 +1,31 @@ +package test + +import ( + "context" + "fmt" +) + +type handler struct{} + +func NewHandler() handler { + return handler{} +} + +func (handler) TakeBasic(ctx context.Context, x string) error { + if x != "latin utf16" { + panic(fmt.Sprintf("unexpected value: `%v`", x)) + } + return nil +} + +func (handler) ReturnUnicode(ctx context.Context) (string, error) { + return "🚀🚀🚀 𠈄𓀀", nil +} + +func (handler) ReturnEmpty(ctx context.Context) (string, error) { + return "", nil +} + +func (handler) Roundtrip(ctx context.Context, x string) (string, error) { + return x, nil +} diff --git a/tests/runtime/strings/test.rs b/tests/runtime/strings/test.rs new file mode 100644 index 000000000..f1004dbcb --- /dev/null +++ b/tests/runtime/strings/test.rs @@ -0,0 +1,21 @@ +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::test::strings::to_test::Handler for Component { + async fn take_basic(&self, _cx: Ctx, s: String) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(s, "latin utf16"); + Ok(()) + } + + async fn return_unicode(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok("🚀🚀🚀 𠈄𓀀".to_string()) + } + + async fn return_empty(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok("".to_string()) + } + + async fn roundtrip(&self, _cx: Ctx, s: String) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(s.clone()) + } +} diff --git a/tests/runtime/strings/test.wit b/tests/runtime/strings/test.wit new file mode 100644 index 000000000..b2d7426e6 --- /dev/null +++ b/tests/runtime/strings/test.wit @@ -0,0 +1,18 @@ +package test:strings; + +interface to-test { + take-basic: func(s: string); + return-unicode: func() -> string; + return-empty: func() -> string; + roundtrip: func(s: string) -> string; +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} diff --git a/tests/runtime/symbol-conflicts/runner.rs b/tests/runtime/symbol-conflicts/runner.rs new file mode 100644 index 000000000..8b19fef35 --- /dev/null +++ b/tests/runtime/symbol-conflicts/runner.rs @@ -0,0 +1,9 @@ +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + crate::runner::my::inline::foo1::foo(wrpc, ()).await?; + crate::runner::my::inline::foo2::foo(wrpc, ()).await?; + crate::runner::my::inline::bar1::bar(wrpc, ()).await?; + crate::runner::my::inline::bar2::bar(wrpc, ()).await?; + Ok(()) +} diff --git a/tests/runtime/symbol-conflicts/test.rs b/tests/runtime/symbol-conflicts/test.rs new file mode 100644 index 000000000..684e46f48 --- /dev/null +++ b/tests/runtime/symbol-conflicts/test.rs @@ -0,0 +1,26 @@ +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::my::inline::foo1::Handler for Component { + async fn foo(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + Ok(()) + } +} + +impl crate::test::exports::my::inline::foo2::Handler for Component { + async fn foo(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + Ok(()) + } +} + +impl crate::test::exports::my::inline::bar1::Handler for Component { + async fn bar(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(String::new()) + } +} + +impl crate::test::exports::my::inline::bar2::Handler for Component { + async fn bar(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(String::new()) + } +} diff --git a/tests/runtime/symbol-conflicts/test.wit b/tests/runtime/symbol-conflicts/test.wit new file mode 100644 index 000000000..b1f292b92 --- /dev/null +++ b/tests/runtime/symbol-conflicts/test.wit @@ -0,0 +1,33 @@ +package my:inline; + +interface foo1 { + foo: func(); +} + +interface foo2 { + foo: func(); +} + +interface bar1 { + bar: func() -> string; +} + +interface bar2 { + bar: func() -> string; +} + +world test { + export foo1; + export foo2; + export bar1; + export bar2; +} + +world runner { + import foo1; + import foo2; + import bar1; + import bar2; + + export run: func(); +} diff --git a/tests/runtime/unused-types/runner.rs b/tests/runtime/unused-types/runner.rs new file mode 100644 index 000000000..f0d00d8cb --- /dev/null +++ b/tests/runtime/unused-types/runner.rs @@ -0,0 +1,15 @@ +//@ args = '--generate-unused-types' + +#[expect(unused_imports)] +use crate::runner::foo::bar::component::UnusedEnum as _; +#[expect(unused_imports)] +use crate::runner::foo::bar::component::UnusedRecord as _; +#[expect(unused_imports)] +use crate::runner::foo::bar::component::UnusedVariant as _; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + crate::runner::foo::bar::component::foo(wrpc, ()).await?; + Ok(()) +} diff --git a/tests/runtime/unused-types/test.rs b/tests/runtime/unused-types/test.rs new file mode 100644 index 000000000..aea9ea84d --- /dev/null +++ b/tests/runtime/unused-types/test.rs @@ -0,0 +1,17 @@ +//@ args = '--generate-unused-types' + +#[expect(unused_imports)] +use crate::test::exports::foo::bar::component::UnusedEnum as _; +#[expect(unused_imports)] +use crate::test::exports::foo::bar::component::UnusedRecord as _; +#[expect(unused_imports)] +use crate::test::exports::foo::bar::component::UnusedVariant as _; + +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::foo::bar::component::Handler for Component { + async fn foo(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + Ok(()) + } +} diff --git a/tests/runtime/unused-types/test.wit b/tests/runtime/unused-types/test.wit new file mode 100644 index 000000000..ad10eddb2 --- /dev/null +++ b/tests/runtime/unused-types/test.wit @@ -0,0 +1,25 @@ +package foo:bar; + +world test { + export component; +} +world runner { + import component; + + export run: func(); +} + +interface component { + variant unused-variant { + %enum(unused-enum), + %record(unused-record) + } + enum unused-enum { + unused + } + record unused-record { + x: u32 + } + + foo: func(); +} diff --git a/tests/runtime/variants/runner.go b/tests/runtime/variants/runner.go new file mode 100644 index 000000000..3a685f37c --- /dev/null +++ b/tests/runtime/variants/runner.go @@ -0,0 +1,158 @@ +package runner + +import ( + "context" + "fmt" + + wrpc "wrpc.io/go" + + "driver/runner/test/variants/to_test" +) + +func Run(ctx context.Context, c wrpc.Invoker) error { + assertEqual(*must(to_test.RoundtripOption(ctx, c, ptr[float32](1.0))), 1) + assertNil(must(to_test.RoundtripOption(ctx, c, nil))) + assertEqual(*must(to_test.RoundtripOption(ctx, c, ptr[float32](2.0))), 2) + + assertEqual(*must(to_test.RoundtripResult(ctx, c, wrpc.Ok[float32, uint32](2))).Ok, 2.0) + assertEqual(*must(to_test.RoundtripResult(ctx, c, wrpc.Ok[float32, uint32](4))).Ok, 4.0) + assertEqual(*must(to_test.RoundtripResult(ctx, c, wrpc.Err[uint32, float32](5.3))).Err, 5) + + assertEqual(must(to_test.RoundtripEnum(ctx, c, to_test.E1_A)), to_test.E1_A) + assertEqual(must(to_test.RoundtripEnum(ctx, c, to_test.E1_B)), to_test.E1_B) + + assertEqual(must(to_test.InvertBool(ctx, c, true)), false) + assertEqual(must(to_test.InvertBool(ctx, c, false)), true) + + { + a1, a2, a3, a4, a5, a6 := must6(to_test.VariantCasts(ctx, c, &to_test.Casts{ + V0: to_test.NewC1A(1), + V1: to_test.NewC2A(2), + V2: to_test.NewC3A(3), + V3: to_test.NewC4A(4), + V4: to_test.NewC5A(5), + V5: to_test.NewC6A(6.0), + })) + assertVariant(a1.GetA()) + assertVariant(a2.GetA()) + assertVariant(a3.GetA()) + assertVariant(a4.GetA()) + assertVariant(a5.GetA()) + assertVariant(a6.GetA()) + } + + { + a1, a2, a3, a4, a5, a6 := must6(to_test.VariantCasts(ctx, c, &to_test.Casts{ + V0: to_test.NewC1B(1), + V1: to_test.NewC2B(2.0), + V2: to_test.NewC3B(3.0), + V3: to_test.NewC4B(4.0), + V4: to_test.NewC5B(5.0), + V5: to_test.NewC6B(6.0), + })) + assertVariant(a1.GetB()) + assertVariant(a2.GetB()) + assertVariant(a3.GetB()) + assertVariant(a4.GetB()) + assertVariant(a5.GetB()) + assertVariant(a6.GetB()) + } + + { + a1, a2, a3, a4 := must4(to_test.VariantZeros(ctx, c, &to_test.Zeros{ + V0: to_test.NewZ1A(1), + V1: to_test.NewZ2A(2), + V2: to_test.NewZ3A(3.0), + V3: to_test.NewZ4A(4.0), + })) + assertVariant(a1.GetA()) + assertVariant(a2.GetA()) + assertVariant(a3.GetA()) + assertVariant(a4.GetA()) + } + + { + a1, a2, a3, a4 := must4(to_test.VariantZeros(ctx, c, &to_test.Zeros{ + V0: to_test.NewZ1B(), + V1: to_test.NewZ2B(), + V2: to_test.NewZ3B(), + V3: to_test.NewZ4B(), + })) + assertEqual(a1.Discriminant(), to_test.Z1B) + assertEqual(a2.Discriminant(), to_test.Z2B) + assertEqual(a3.Discriminant(), to_test.Z3B) + assertEqual(a4.Discriminant(), to_test.Z4B) + } + + must0(to_test.VariantTypedefs(ctx, c, nil, false, wrpc.Err[uint32, struct{}](struct{}{}))) + + { + a, b, cc := must3(to_test.VariantEnums(ctx, c, true, wrpc.Ok[struct{}, struct{}](struct{}{}), to_test.MyErrno_Success)) + assertEqual(a, true) + assert(b.Ok != nil) + assertEqual(cc, to_test.MyErrno_Success) + } + return nil +} + +func ptr[T any](v T) *T { + return &v +} + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +func must0(err error) { + if err != nil { + panic(err) + } +} + +func must3[A, B, C any](a A, b B, c C, err error) (A, B, C) { + if err != nil { + panic(err) + } + return a, b, c +} + +func must4[A, B, C, D any](a A, b B, c C, d D, err error) (A, B, C, D) { + if err != nil { + panic(err) + } + return a, b, c, d +} + +func must6[A, B, C, D, E, F any](a A, b B, c C, d D, e E, f F, err error) (A, B, C, D, E, F) { + if err != nil { + panic(err) + } + return a, b, c, d, e, f +} + +func assertVariant[T any](_ T, ok bool) { + if !ok { + panic("unexpected variant discriminant") + } +} + +func assertNil[T any](v *T) { + if v != nil { + panic(fmt.Sprintf("expected nil, got %v", *v)) + } +} + +func assertEqual[T comparable](a T, b T) { + if a != b { + panic(fmt.Sprintf("%v not equal to %v", a, b)) + } +} + +func assert(v bool) { + if !v { + panic("assertion failed") + } +} diff --git a/tests/runtime/variants/runner.rs b/tests/runtime/variants/runner.rs new file mode 100644 index 000000000..683581236 --- /dev/null +++ b/tests/runtime/variants/runner.rs @@ -0,0 +1,72 @@ +use crate::runner::test::variants::to_test::*; + +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + assert_eq!(roundtrip_option(wrpc, (), Some(1.0)).await?, Some(1)); + assert_eq!(roundtrip_option(wrpc, (), None).await?, None); + assert_eq!(roundtrip_option(wrpc, (), Some(2.0)).await?, Some(2)); + assert_eq!(roundtrip_result(wrpc, (), &Ok(2)).await?, Ok(2.0)); + assert_eq!(roundtrip_result(wrpc, (), &Ok(4)).await?, Ok(4.0)); + assert_eq!(roundtrip_result(wrpc, (), &Err(5.3)).await?, Err(5)); + + assert_eq!(roundtrip_enum(wrpc, (), E1::A).await?, E1::A); + assert_eq!(roundtrip_enum(wrpc, (), E1::B).await?, E1::B); + + assert_eq!(invert_bool(wrpc, (), true).await?, false); + assert_eq!(invert_bool(wrpc, (), false).await?, true); + + let (a1, a2, a3, a4, a5, a6) = variant_casts( + wrpc, + (), + (C1::A(1), C2::A(2), C3::A(3), C4::A(4), C5::A(5), C6::A(6.0)), + ) + .await?; + assert!(matches!(a1, C1::A(1))); + assert!(matches!(a2, C2::A(2))); + assert!(matches!(a3, C3::A(3))); + assert!(matches!(a4, C4::A(4))); + assert!(matches!(a5, C5::A(5))); + assert!(matches!(a6, C6::A(b) if b == 6.0)); + + let (a1, a2, a3, a4, a5, a6) = variant_casts( + wrpc, + (), + ( + C1::B(1), + C2::B(2.0), + C3::B(3.0), + C4::B(4.0), + C5::B(5.0), + C6::B(6.0), + ), + ) + .await?; + assert!(matches!(a1, C1::B(1))); + assert!(matches!(a2, C2::B(b) if b == 2.0)); + assert!(matches!(a3, C3::B(b) if b == 3.0)); + assert!(matches!(a4, C4::B(b) if b == 4.0)); + assert!(matches!(a5, C5::B(b) if b == 5.0)); + assert!(matches!(a6, C6::B(b) if b == 6.0)); + + let (a1, a2, a3, a4) = + variant_zeros(wrpc, (), (Z1::A(1), Z2::A(2), Z3::A(3.0), Z4::A(4.0))).await?; + assert!(matches!(a1, Z1::A(1))); + assert!(matches!(a2, Z2::A(2))); + assert!(matches!(a3, Z3::A(b) if b == 3.0)); + assert!(matches!(a4, Z4::A(b) if b == 4.0)); + + let (a1, a2, a3, a4) = variant_zeros(wrpc, (), (Z1::B, Z2::B, Z3::B, Z4::B)).await?; + assert!(matches!(a1, Z1::B)); + assert!(matches!(a2, Z2::B)); + assert!(matches!(a3, Z3::B)); + assert!(matches!(a4, Z4::B)); + + variant_typedefs(wrpc, (), None, false, &Err(())).await?; + + assert_eq!( + variant_enums(wrpc, (), true, &Ok(()), MyErrno::Success).await?, + (true, Ok(()), MyErrno::Success) + ); + Ok(()) +} diff --git a/tests/runtime/variants/test.go b/tests/runtime/variants/test.go new file mode 100644 index 000000000..af94848be --- /dev/null +++ b/tests/runtime/variants/test.go @@ -0,0 +1,54 @@ +package test + +import ( + "context" + + wrpc "wrpc.io/go" + + "driver/test/exports/test/variants/to_test" +) + +type handler struct{} + +func NewHandler() handler { + return handler{} +} + +func (handler) RoundtripOption(ctx context.Context, a *float32) (*uint8, error) { + if a == nil { + return nil, nil + } + v := uint8(*a) + return &v, nil +} + +func (handler) RoundtripResult(ctx context.Context, a *wrpc.Result[uint32, float32]) (*wrpc.Result[float64, uint8], error) { + if a.Ok != nil { + return wrpc.Ok[uint8, float64](float64(*a.Ok)), nil + } + return wrpc.Err[float64, uint8](uint8(*a.Err)), nil +} + +func (handler) RoundtripEnum(ctx context.Context, a to_test.E1) (to_test.E1, error) { + return a, nil +} + +func (handler) InvertBool(ctx context.Context, a bool) (bool, error) { + return !a, nil +} + +func (handler) VariantCasts(ctx context.Context, a *to_test.Casts) (*to_test.C1, *to_test.C2, *to_test.C3, *to_test.C4, *to_test.C5, *to_test.C6, error) { + return a.V0, a.V1, a.V2, a.V3, a.V4, a.V5, nil +} + +func (handler) VariantZeros(ctx context.Context, a *to_test.Zeros) (*to_test.Z1, *to_test.Z2, *to_test.Z3, *to_test.Z4, error) { + return a.V0, a.V1, a.V2, a.V3, nil +} + +func (handler) VariantTypedefs(ctx context.Context, a *uint32, b bool, c *to_test.ResultTypedef) error { + return nil +} + +func (handler) VariantEnums(ctx context.Context, a bool, b *wrpc.Result[struct{}, struct{}], c to_test.MyErrno) (bool, *wrpc.Result[struct{}, struct{}], to_test.MyErrno, error) { + return a, b, c, nil +} diff --git a/tests/runtime/variants/test.rs b/tests/runtime/variants/test.rs new file mode 100644 index 000000000..01572e329 --- /dev/null +++ b/tests/runtime/variants/test.rs @@ -0,0 +1,70 @@ +use crate::test::exports::test::variants::to_test::*; + +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::test::variants::to_test::Handler for Component { + async fn roundtrip_option( + &self, + _cx: Ctx, + a: Option, + ) -> ::wit_bindgen_wrpc::anyhow::Result> { + Ok(a.map(|x| x as u8)) + } + + async fn roundtrip_result( + &self, + _cx: Ctx, + a: Result, + ) -> ::wit_bindgen_wrpc::anyhow::Result> { + Ok(match a { + Ok(a) => Ok(a.into()), + Err(b) => Err(b as u8), + }) + } + + async fn roundtrip_enum(&self, _cx: Ctx, a: E1) -> ::wit_bindgen_wrpc::anyhow::Result { + assert_eq!(a, a); + Ok(a) + } + + async fn invert_bool(&self, _cx: Ctx, a: bool) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(!a) + } + + async fn variant_casts( + &self, + _cx: Ctx, + a: Casts, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn variant_zeros( + &self, + _cx: Ctx, + a: Zeros, + ) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(a) + } + + async fn variant_typedefs( + &self, + _cx: Ctx, + _a: Option, + _b: bool, + _c: Result, + ) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + Ok(()) + } + + async fn variant_enums( + &self, + _cx: Ctx, + a: bool, + b: Result<(), ()>, + c: MyErrno, + ) -> ::wit_bindgen_wrpc::anyhow::Result<(bool, Result<(), ()>, MyErrno)> { + Ok((a, b, c)) + } +} diff --git a/tests/runtime/variants/test.wit b/tests/runtime/variants/test.wit new file mode 100644 index 000000000..5416afaba --- /dev/null +++ b/tests/runtime/variants/test.wit @@ -0,0 +1,45 @@ +package test:variants; + +interface to-test { + roundtrip-option: func(a: option) -> option; + roundtrip-result: func(a: result) -> result; + + enum e1 { a, b } + roundtrip-enum: func(a: e1) -> e1; + + invert-bool: func(a: bool) -> bool; + + variant c1 { a(s32), b(s64) } + variant c2 { a(s32), b(f32) } + variant c3 { a(s32), b(f64) } + variant c4 { a(s64), b(f32) } + variant c5 { a(s64), b(f64) } + variant c6 { a(f32), b(f64) } + type casts = tuple; + variant-casts: func(a: casts) -> casts; + + variant z1 { a(s32), b } + variant z2 { a(s64), b } + variant z3 { a(f32), b } + variant z4 { a(f64), b } + type zeros = tuple; + variant-zeros: func(a: zeros) -> zeros; + + type option-typedef = option; + type bool-typedef = bool; + type result-typedef = result; + variant-typedefs: func(a: option-typedef, b: bool-typedef, c: result-typedef); + + enum my-errno { success, a, b } + variant-enums: func(a: bool, b: result, c: my-errno) -> tuple; +} + +world runner { + import to-test; + + export run: func(); +} + +world test { + export to-test; +} diff --git a/tests/runtime/versions/deps/v1/v1.wit b/tests/runtime/versions/deps/v1/v1.wit new file mode 100644 index 000000000..0a58bc6a6 --- /dev/null +++ b/tests/runtime/versions/deps/v1/v1.wit @@ -0,0 +1,6 @@ +package test:dep@0.1.0; + +interface test { + x: func() -> f32; + y: func(a: f32) -> f32; +} diff --git a/tests/runtime/versions/deps/v2/v2.wit b/tests/runtime/versions/deps/v2/v2.wit new file mode 100644 index 000000000..8bc030571 --- /dev/null +++ b/tests/runtime/versions/deps/v2/v2.wit @@ -0,0 +1,6 @@ +package test:dep@0.2.0; + +interface test { + x: func() -> f32; + z: func(a: f32, b: f32) -> f32; +} diff --git a/tests/runtime/versions/runner.rs b/tests/runtime/versions/runner.rs new file mode 100644 index 000000000..08e39b333 --- /dev/null +++ b/tests/runtime/versions/runner.rs @@ -0,0 +1,12 @@ +pub async fn run( + wrpc: &impl ::wit_bindgen_wrpc::wrpc_transport::Invoke, +) -> ::wit_bindgen_wrpc::anyhow::Result<()> { + use crate::runner::test::dep0_1_0::test as v1; + assert_eq!(v1::x(wrpc, ()).await?, 1.0); + assert_eq!(v1::y(wrpc, (), 1.0).await?, 2.0); + + use crate::runner::test::dep0_2_0::test as v2; + assert_eq!(v2::x(wrpc, ()).await?, 2.0); + assert_eq!(v2::z(wrpc, (), 1.0, 1.0).await?, 4.0); + Ok(()) +} diff --git a/tests/runtime/versions/test.rs b/tests/runtime/versions/test.rs new file mode 100644 index 000000000..af88ce662 --- /dev/null +++ b/tests/runtime/versions/test.rs @@ -0,0 +1,22 @@ +#[derive(Clone)] +pub struct Component; + +impl crate::test::exports::test::dep0_1_0::test::Handler for Component { + async fn x(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(1.0) + } + + async fn y(&self, _cx: Ctx, a: f32) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(1.0 + a) + } +} + +impl crate::test::exports::test::dep0_2_0::test::Handler for Component { + async fn x(&self, _cx: Ctx) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(2.0) + } + + async fn z(&self, _cx: Ctx, a: f32, b: f32) -> ::wit_bindgen_wrpc::anyhow::Result { + Ok(2.0 + a + b) + } +} diff --git a/tests/runtime/versions/test.wit b/tests/runtime/versions/test.wit new file mode 100644 index 000000000..68fbaaabf --- /dev/null +++ b/tests/runtime/versions/test.wit @@ -0,0 +1,13 @@ +package test:versions; + +world runner { + import test:dep/test@0.1.0; + import test:dep/test@0.2.0; + + export run: func(); +} + +world test { + export test:dep/test@0.1.0; + export test:dep/test@0.2.0; +}