Skip to content

Commit 5687f72

Browse files
test(jco): implement tests for stream lower values
1 parent f8dd481 commit 5687f72

5 files changed

Lines changed: 172 additions & 47 deletions

File tree

crates/test-components/src/bin/stream_rx.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,53 @@ impl use_stream_async::Guest for Component {
2424
rx
2525
}
2626

27+
async fn read_stream_values_bool(rx: StreamReader<bool>) -> Vec<bool> {
28+
read_async_values(rx).await
29+
}
30+
31+
async fn read_stream_values_u8(rx: StreamReader<u8>) -> Vec<u8> {
32+
read_async_values(rx).await
33+
}
34+
35+
async fn read_stream_values_s8(rx: StreamReader<i8>) -> Vec<i8> {
36+
read_async_values(rx).await
37+
}
38+
39+
async fn read_stream_values_u16(rx: StreamReader<u16>) -> Vec<u16> {
40+
read_async_values(rx).await
41+
}
42+
43+
async fn read_stream_values_s16(rx: StreamReader<i16>) -> Vec<i16> {
44+
read_async_values(rx).await
45+
}
46+
2747
async fn read_stream_values_u32(rx: StreamReader<u32>) -> Vec<u32> {
2848
read_async_values(rx).await
2949
}
3050

3151
async fn read_stream_values_s32(rx: StreamReader<i32>) -> Vec<i32> {
3252
read_async_values(rx).await
3353
}
54+
55+
async fn read_stream_values_u64(rx: StreamReader<u64>) -> Vec<u64> {
56+
read_async_values(rx).await
57+
}
58+
59+
async fn read_stream_values_s64(rx: StreamReader<i64>) -> Vec<i64> {
60+
read_async_values(rx).await
61+
}
62+
63+
async fn read_stream_values_f32(rx: StreamReader<f32>) -> Vec<f32> {
64+
read_async_values(rx).await
65+
}
66+
67+
async fn read_stream_values_f64(rx: StreamReader<f64>) -> Vec<f64> {
68+
read_async_values(rx).await
69+
}
70+
71+
async fn read_stream_values_string(rx: StreamReader<String>) -> Vec<String> {
72+
read_async_values(rx).await
73+
}
3474
}
3575

3676
async fn read_async_values<T>(mut rx: StreamReader<T>) -> Vec<T> {

crates/test-components/wit/all.wit

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,7 @@ interface resources {
2323
}
2424
}
2525

26-
interface get-stream-async {
27-
use resources.{example-resource};
28-
29-
resource example-guest-resource {
30-
constructor(id: u32);
31-
get-id: func() -> u32;
32-
get-id-async: async func() -> u32;
33-
}
34-
26+
interface example-types {
3527
variant example-variant {
3628
num(u32),
3729
str(string),
@@ -55,6 +47,17 @@ interface get-stream-async {
5547
second,
5648
third,
5749
}
50+
}
51+
52+
interface get-stream-async {
53+
use resources.{example-resource};
54+
use example-types.{example-variant, example-enum, example-record, example-flags};
55+
56+
resource example-guest-resource {
57+
constructor(id: u32);
58+
get-id: func() -> u32;
59+
get-id-async: async func() -> u32;
60+
}
5861

5962
get-stream-u32: async func(vals: list<u32>) -> result<stream<u32>, string>;
6063
get-stream-s32: async func(vals: list<s32>) -> result<stream<s32>, string>;
@@ -179,17 +182,68 @@ world basic-run-string {
179182
//////////////////
180183

181184
interface use-stream-sync {
185+
// TODO(fix): optimize host-only stream usage -- detect when the read & write are held by the host
182186
stream-passthrough: func(s: stream<u32>) -> stream<u32>;
183187
}
184188

185189
interface use-stream-async {
190+
// use resources.{example-resource};
191+
use example-types.{example-variant, example-enum, example-record, example-flags};
192+
186193
stream-passthrough: async func(s: stream<u32>) -> stream<u32>;
187194

188195
read-stream-values-u32: async func(s: stream<u32>) -> list<u32>;
189196
read-stream-values-s32: async func(s: stream<s32>) -> list<s32>;
197+
198+
read-stream-values-u8: async func(s: stream<u8>) -> list<u8>;
199+
read-stream-values-s8: async func(s: stream<s8>) -> list<s8>;
200+
201+
read-stream-values-u16: async func(s: stream<u16>) -> list<u16>;
202+
read-stream-values-s16: async func(s: stream<s16>) -> list<s16>;
203+
204+
read-stream-values-u64: async func(s: stream<u64>) -> list<u64>;
205+
read-stream-values-s64: async func(s: stream<s64>) -> list<s64>;
206+
207+
read-stream-values-f32: async func(s: stream<f32>) -> list<f32>;
208+
209+
read-stream-values-f64: async func(s: stream<f64>) -> list<f64>;
210+
211+
read-stream-values-bool: async func(s: stream<bool>) -> list<bool>;
212+
213+
read-stream-values-string: async func(s: stream<string>) -> list<string>;
214+
215+
// read-stream-values-record: async func(s: stream<example-record>) -> list<example-record>;
216+
217+
// read-stream-values-variant: async func(s: stream<example-variant>) -> list<example-variant>;
218+
219+
// read-stream-values-list-u8: async func(s: stream<list<u8>>) -> list<list<u8>>;
220+
// read-stream-values-list-string: async func(s: stream<list<string>>) -> list<list<string>>;
221+
// read-stream-values-list-record: async func(s: stream<list<example-record>>) -> list<list<example-record>>;
222+
// read-stream-values-fixed-list-u32: async func(s: stream<list<list<u32, 5>>>) -> list<list<list<u32, 5>>>;
223+
224+
// read-stream-values-tuple: async func(s: stream<tuple<u32, s32, string>>) -> list<tuple<u32, s32, string>>;
225+
226+
// read-stream-values-flags: async func(s: stream<example-flags>) -> list<example-flags>;
227+
228+
// read-stream-values-enum: async func(s: stream<example-enum>) -> list<example-enum>;
229+
230+
// read-stream-values-option-string: async func(s: stream<option<string>>) -> list<option<string>>;
231+
232+
// read-stream-values-result-string: async func(s: stream<result<string, string>>) -> list<result<string, string>>;
233+
234+
// // NOTE: the check here will be whether the resources are disposed inside the component properly
235+
// read-stream-values-example-resource-own: async func(s: stream<example-resource>);
236+
237+
// read-stream-values-example-resource-own-attr: async func(s: stream<example-resource>) -> list<u32>;
238+
239+
// read-stream-values-stream-string: async func(s: stream<stream<string>>) -> list<list<string>>;
240+
241+
//read-stream-values-future-string: async func(vals: list<future<string>>) -> result<stream<future<string>>, string>;
190242
}
191243

192244
world stream-rx {
245+
// import resources;
246+
193247
export use-stream-sync;
194248
export use-stream-async;
195249
}

packages/jco/test/common.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { readdir } from "node:fs/promises";
33
import { join } from "node:path";
44
import { fileURLToPath, URL } from "node:url";
55

6+
import { assert } from "vitest";
7+
68
/** Path to a linter as installed by npm-compatible tooling */
79
export const LINTER_PATH = fileURLToPath(new URL("../../../node_modules/oxlint/bin/oxlint", import.meta.url));
810

@@ -48,3 +50,29 @@ export async function getDefaultComponentFixtures() {
4850
.filter((f) => f.isFile() && f.name !== "dummy_reactor.component.wasm")
4951
.map((f) => f.name);
5052
}
53+
54+
/** Check the values of a given stream (normally returned from a component) */
55+
export async function checkStreamValues(args) {
56+
const { stream, vals, typeName, assertEqFn, partial } = args ?? {};
57+
const expectedValues = args.expectedValues ?? [];
58+
59+
// Ensure the values produced match expected
60+
const eq = assertEqFn ?? assert.equal;
61+
let iteratorRes;
62+
for (const [idx, v] of vals.entries()) {
63+
const expected = expectedValues[idx] ?? v;
64+
iteratorRes = await stream.next();
65+
assert.isFalse(iteratorRes.done);
66+
eq(iteratorRes.value, expected, `${typeName} [${idx}] read is incorrect`);
67+
}
68+
69+
// If dealing with a partial list of values from the stream, do not attempt to read the last value
70+
if (partial) {
71+
return;
72+
}
73+
74+
// Ensure the next value is undefined (and the iterator is done)
75+
iteratorRes = await stream.next();
76+
assert.isUndefined(iteratorRes.value);
77+
assert.isTrue(iteratorRes.done);
78+
}

packages/jco/test/p3/stream-lifts.js

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { join } from "node:path";
33
import { suite, test, assert, beforeAll, beforeEach, afterAll, expect } from "vitest";
44

55
import { setupAsyncTest } from "../helpers.js";
6-
import { AsyncFunction, LOCAL_TEST_COMPONENTS_DIR } from "../common.js";
6+
import { AsyncFunction, LOCAL_TEST_COMPONENTS_DIR, checkStreamValues } from "../common.js";
77
import { WASIShim } from "@bytecodealliance/preview2-shim/instantiation";
88

99
suite("stream<T> lifts", () => {
@@ -124,9 +124,9 @@ suite("stream<T> lifts", () => {
124124

125125
let invalidVals = [-1, 256];
126126
for (const invalid of invalidVals) {
127-
await expect(() =>
128-
instance["jco:test-components/get-stream-async"].getStreamU8([invalid]),
129-
).rejects.toThrow(/invalid u8 value/);
127+
await expect(() => instance["jco:test-components/get-stream-async"].getStreamU8([invalid])).rejects.toThrow(
128+
/invalid u8 value/,
129+
);
130130
}
131131

132132
vals = [-11, -22, -33, -128, 127];
@@ -135,9 +135,9 @@ suite("stream<T> lifts", () => {
135135

136136
invalidVals = [-129, 128];
137137
for (const invalid of invalidVals) {
138-
await expect(() =>
139-
instance["jco:test-components/get-stream-async"].getStreamS8([invalid]),
140-
).rejects.toThrow(/invalid s8 value/);
138+
await expect(() => instance["jco:test-components/get-stream-async"].getStreamS8([invalid])).rejects.toThrow(
139+
/invalid s8 value/,
140+
);
141141
}
142142
});
143143

@@ -291,6 +291,7 @@ suite("stream<T> lifts", () => {
291291
assert.instanceOf(instance["jco:test-components/get-stream-async"].getStreamListU8, AsyncFunction);
292292
let vals = [[0x01, 0x02, 0x03, 0x04, 0x05], new Uint8Array([0x05, 0x04, 0x03, 0x02, 0x01]), []];
293293
let stream = await instance["jco:test-components/get-stream-async"].getStreamListU8(vals);
294+
294295
await checkStreamValues({
295296
stream,
296297
vals,
@@ -305,6 +306,8 @@ suite("stream<T> lifts", () => {
305306
});
306307
});
307308

309+
// TODO(fix): add tests for optimized UintXArrays (js_array_ty)
310+
308311
test.concurrent("list<string>", async () => {
309312
assert.instanceOf(instance["jco:test-components/get-stream-async"].getStreamListString, AsyncFunction);
310313
let vals = [["first", "second", "third"], []];
@@ -429,28 +432,3 @@ suite("stream<T> lifts", () => {
429432
assert.isUndefined(value);
430433
});
431434
});
432-
433-
async function checkStreamValues(args) {
434-
const { stream, vals, typeName, assertEqFn, partial } = args ?? {};
435-
const expectedValues = args.expectedValues ?? [];
436-
437-
// Ensure the values produced match expected
438-
const eq = assertEqFn ?? assert.equal;
439-
let iteratorRes;
440-
for (const [idx, v] of vals.entries()) {
441-
const expected = expectedValues[idx] ?? v;
442-
iteratorRes = await stream.next();
443-
assert.isFalse(iteratorRes.done);
444-
eq(iteratorRes.value, expected, `${typeName} [${idx}] read is incorrect`);
445-
}
446-
447-
// If dealing with a partial list of values from the stream, do not attempt to read the last value
448-
if (partial) {
449-
return;
450-
}
451-
452-
// Ensure the next value is undefined (and the iterator is done)
453-
iteratorRes = await stream.next();
454-
assert.isUndefined(iteratorRes.value);
455-
assert.isTrue(iteratorRes.done);
456-
}

packages/jco/test/p3/stream-lowers.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ReadableStream } from "node:stream/web";
44
import { suite, test, assert, beforeAll, beforeEach, afterAll } from "vitest";
55

66
import { setupAsyncTest } from "../helpers.js";
7-
import { AsyncFunction, LOCAL_TEST_COMPONENTS_DIR } from "../common.js";
7+
import { AsyncFunction, LOCAL_TEST_COMPONENTS_DIR, checkStreamValues } from "../common.js";
88
import { WASIShim } from "@bytecodealliance/preview2-shim/instantiation";
99

1010
suite("stream<T> lowers", () => {
@@ -109,14 +109,39 @@ suite("stream<T> lowers", () => {
109109
assert.deepEqual(vals, returnedVals);
110110
});
111111

112-
test.skip("u32/s32", async () => {
112+
test.concurrent("u32/s32", async () => {
113113
assert.instanceOf(instance["jco:test-components/use-stream-async"].readStreamValuesU32, AsyncFunction);
114114
assert.instanceOf(instance["jco:test-components/use-stream-async"].readStreamValuesS32, AsyncFunction);
115115

116-
// TODO: same as async pass through test except we get the list back and can compare directly
116+
let vals = [10, 5, 0];
117+
let returnedVals = await instance["jco:test-components/use-stream-async"].readStreamValuesU32(
118+
createReadableStreamFromValues(vals),
119+
);
120+
assert.deepEqual(returnedVals, vals);
121+
122+
vals = [-32, 90001, 3200000];
123+
returnedVals = await instance["jco:test-components/use-stream-async"].readStreamValuesS32(
124+
createReadableStreamFromValues(vals),
125+
);
126+
assert.deepEqual(returnedVals, vals);
127+
});
128+
129+
test.only("bool", async () => {
130+
assert.instanceOf(instance["jco:test-components/use-stream-async"].readStreamValuesBool, AsyncFunction);
117131

118-
// TODO:
119-
// let vals = [11, 22, 33];
120-
// let stream = await instance["jco:test-components/get-stream-async"].getStreamU32(vals);
132+
let vals = [true, false];
133+
let returnedVals = await instance["jco:test-components/use-stream-async"].readStreamValuesBool(
134+
createReadableStreamFromValues(vals),
135+
);
136+
assert.deepEqual(returnedVals, vals);
121137
});
122138
});
139+
140+
function createReadableStreamFromValues(vals) {
141+
return new ReadableStream({
142+
start(ctrl) {
143+
vals.forEach((v) => ctrl.enqueue(v));
144+
ctrl.close();
145+
},
146+
});
147+
}

0 commit comments

Comments
 (0)