Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
14e660e
chore: copy wit-bindgen 0.58 runtime tests
rvolosatovs Jun 23, 2026
bb5de33
test(flavorful): port wit-bindgen 0.58 runtime test to wRPC harness
rvolosatovs Jun 24, 2026
205f492
test: port wit-bindgen 0.58 runtime tests (options, records, variants…
rvolosatovs Jun 24, 2026
d964a1f
test(harness): resolve deps/ dirs in runtime test loader
rvolosatovs Jun 24, 2026
621432e
test: port wit-bindgen 0.58 resource runtime tests to wRPC harness
rvolosatovs Jun 24, 2026
0239469
test: drop wac/multi-component and wat demo runtime tests
rvolosatovs Jun 24, 2026
6652521
test: drop legacy rust runtime tests that need unimplemented features
rvolosatovs Jun 24, 2026
d90ea79
test: port feasible legacy rust runtime tests to wRPC harness
rvolosatovs Jun 24, 2026
4ac3758
test: consume published wasm-tokio 0.6.1 leb128 fix
rvolosatovs Jun 24, 2026
d290631
fix(rust): avoid generated handle generic colliding with WIT type `c`
rvolosatovs Jun 24, 2026
549ba20
test(alternative-bitflags): exercise a custom --bitflags-path
rvolosatovs Jun 24, 2026
16c1e52
fix: correctly update `wasm-tokio`
rvolosatovs Jun 24, 2026
31cea06
test(harness): mark map and fixed-length-lists runtime tests as xfail
rvolosatovs Jun 24, 2026
bcf1c14
test(harness): run Go runtime tests over the TCP transport
rvolosatovs Jun 24, 2026
b031154
fix(go): correct flags overflow check for byte-aligned flag counts
rvolosatovs Jun 24, 2026
2530330
test: port wit-bindgen 0.58 runtime test Go sources to wRPC host API
rvolosatovs Jun 24, 2026
54005ff
chore: run fmt
rvolosatovs Jun 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
11 changes: 10 additions & 1 deletion crates/wit-bindgen-go/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3599,14 +3599,23 @@ 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#"
if (p[{}] >> {}) > 0 {{
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");

Expand Down
2 changes: 1 addition & 1 deletion crates/wit-bindgen-rust/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
140 changes: 139 additions & 1 deletion crates/wit-bindgen-test/src/go.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -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")
}
}
48 changes: 35 additions & 13 deletions crates/wit-bindgen-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<Component>> {
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);

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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::<Vec<_>>();
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
Expand Down Expand Up @@ -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),
}
}

Expand Down Expand Up @@ -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<T, N>` 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<T>(&self, result: &Result<T>, should_fail: bool) {
if result.is_ok() == !should_fail {
Expand Down
74 changes: 74 additions & 0 deletions tests/runtime/fixed-length-lists/runner.rs
Original file line number Diff line number Diff line change
@@ -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]);
}
}
}
Loading
Loading