From 866a2b87db56e04cee84733edc03e850a1b9e371 Mon Sep 17 00:00:00 2001 From: Zak Stucke Date: Wed, 31 Jul 2024 11:57:57 +0100 Subject: [PATCH] Split tests --- .../tests/transferable_contended.rs | 61 ++++ leptos_workers/tests/transferable_types.rs | 288 +++++++++++++++ leptos_workers/tests/worker.rs | 327 ------------------ scripts/test.sh | 4 +- 4 files changed, 352 insertions(+), 328 deletions(-) create mode 100644 leptos_workers/tests/transferable_contended.rs create mode 100644 leptos_workers/tests/transferable_types.rs diff --git a/leptos_workers/tests/transferable_contended.rs b/leptos_workers/tests/transferable_contended.rs new file mode 100644 index 0000000..f02c8ee --- /dev/null +++ b/leptos_workers/tests/transferable_contended.rs @@ -0,0 +1,61 @@ +/// NOTE: this test doesn't do much until underlying worker contention issue fixed. +/// Once fixed, increasing num_calls = x will "properly" enable this test. +/// At the moment it would error after an increase with a generic worker issue. +use futures::future::join_all; +use leptos_workers_macro::worker; +use serde::{Deserialize, Serialize}; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); + +/// Seeded simple random byte generator fns. +fn seeded_rand_byte() -> impl FnMut() -> u8 { + let mut next = 0; + move || { + next = (next + 1) % 255; + next + } +} + +#[derive(Clone, Serialize, Deserialize)] +struct TransMsgArrWithCheck( + #[serde(with = "leptos_workers::transferable")] js_sys::Uint8Array, + Vec, +); + +impl TransMsgArrWithCheck { + pub fn check(&self) { + assert_eq!(self.0.length(), self.1.len() as u32); + for (i, byte) in self.1.iter().enumerate() { + assert_eq!(self.0.get_index(i as u32), *byte); + } + } +} + +#[worker] +async fn worker_contended(req: TransMsgArrWithCheck) -> TransMsgArrWithCheck { + req.check(); + req +} + +#[wasm_bindgen_test] +async fn test_transferable_whilst_contended() { + // Will concurrently call the worker this many times with varying requests: + let num_calls = 3; + + let mut get_next_byte = seeded_rand_byte(); + let mut reqs = Vec::with_capacity(num_calls); + for _ in 0..num_calls { + let mut vec = Vec::with_capacity(10); + for _ in 0..10 { + vec.push(get_next_byte()); + } + let js_arr = js_sys::Uint8Array::new_with_length(10); + js_arr.copy_from(&vec); + reqs.push(TransMsgArrWithCheck(js_arr, vec)); + } + // Process all via join simulating concurrency/contended serialization/deserialization: + for result in join_all(reqs.into_iter().map(worker_contended)).await { + result.unwrap().check(); + } +} diff --git a/leptos_workers/tests/transferable_types.rs b/leptos_workers/tests/transferable_types.rs new file mode 100644 index 0000000..67c6659 --- /dev/null +++ b/leptos_workers/tests/transferable_types.rs @@ -0,0 +1,288 @@ +use std::collections::HashMap; + +use js_sys::Uint8Array; +use leptos_workers_macro::worker; +use serde::{Deserialize, Serialize}; +use wasm_bindgen::{JsCast, JsValue}; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); + +/// Seeded simple random byte generator fns. +fn seeded_rand_byte() -> impl FnMut() -> u8 { + let mut next = 0; + move || { + next = (next + 1) % 255; + next + } +} + +/// Standard worker test, passing an object containing transferables to a worker and back again. +macro_rules! transferable_test { + ($id:ident, $typ:ty, $create:block, $verify:expr) => { + paste::item! { + #[allow(non_camel_case_types)] + #[derive(Clone, Serialize, Deserialize)] + struct [< TransMsg_ $id >](#[serde(with = "leptos_workers::transferable")] $typ); + + #[worker] + async fn [< worker_fn_ $id >](req: [< TransMsg_ $id >]) -> [< TransMsg_ $id >] { + ($verify)(req.0.clone()); + req + } + + #[wasm_bindgen_test] + async fn [< test_transferable_ $id >]() { + let contents = $create; + ($verify)(contents.clone()); + let response_msg = [< worker_fn_ $id >]([< TransMsg_ $id >](contents)) + .await + .unwrap(); + ($verify)(response_msg.0); + } + } + }; +} + +transferable_test! { + complex, + HashMap>>>, + { + let mut get_next_byte = seeded_rand_byte(); + let mut map = HashMap::new(); + for x in 0..10 { + let mut vec = Vec::new(); + for _y in 0..10 { + let mut inner_map = HashMap::new(); + for z in 0..10 { + let uint8_array = js_sys::Uint8Array::new(&js_sys::ArrayBuffer::new(10)); + for w in 0..10 { + uint8_array.set_index(w as u32, get_next_byte()); + } + inner_map.insert(z.to_string(), Some(uint8_array)); + } + vec.push(inner_map); + } + map.insert(x.to_string(), vec); + } + map + }, + |map: HashMap>>>| { + let mut get_next_byte = seeded_rand_byte(); + + assert_eq!(map.len(), 10); + for x in 0..10 { + let vec = map.get(&x.to_string()).unwrap(); + assert_eq!(vec.len(), 10); + for inner_map in vec.iter() { + assert_eq!(inner_map.len(), 10); + for z in 0..10 { + let uint8_array = inner_map.get(&z.to_string()).unwrap().as_ref().unwrap(); + assert_eq!(uint8_array.length(), 10); + for w in 0..10 { + assert_eq!(uint8_array.get_index(w as u32), get_next_byte()); + } + } + } + } + } +} + +transferable_test! { + uint8_array, + js_sys::Uint8Array, + { + let uint8_array = js_sys::Uint8Array::new(&js_sys::ArrayBuffer::new(10)); + for x in 0..10 { + uint8_array.set_index(x as u32, x); + } + uint8_array + }, + |arr: js_sys::Uint8Array| { + assert_eq!(arr.length(), 10); + for x in 0..10 { + assert_eq!(arr.get_index(x as u32), x); + } + } +} + +transferable_test! { + arb_js, + JsValue, + { + let js_val: JsValue = js_sys::ArrayBuffer::new(10).into(); + js_val + }, + |val: JsValue| { + let arr = val.dyn_into::().unwrap(); + assert_eq!(arr.byte_length(), 10); + } +} + +transferable_test! { + option_t_some, + Option, + { + Some(js_sys::ArrayBuffer::new(10)) + }, + |opt: Option| { + let arr = opt.unwrap(); + assert_eq!(arr.byte_length(), 10); + } +} + +transferable_test! { + option_t_none, + Option, + { + None + }, + |opt: Option| { + assert!(opt.is_none()); + } +} + +transferable_test! { + vec_of_t, + Vec, + { + // 10 items, each arr with 10 elements, all ordered by index: + let mut vec = Vec::new(); + for x in 0..10 { + let uint8_array = js_sys::Uint8Array::new(&js_sys::ArrayBuffer::new(10)); + for y in 0..10 { + uint8_array.set_index(y as u32, x * 10 + y); + } + vec.push(uint8_array); + } + vec + }, + |vec: Vec| { + assert_eq!(vec.len(), 10); + for (x, arr) in vec.into_iter().enumerate() { + assert_eq!(arr.length(), 10); + for y in 0..10 { + assert_eq!(arr.get_index(y as u32), (x * 10 + y) as u8); + } + } + } +} + +transferable_test! { + hashmap, + HashMap, + { + let mut map = HashMap::new(); + for x in 0..10 { + let uint8_array = js_sys::Uint8Array::new(&js_sys::ArrayBuffer::new(10)); + for y in 0..10 { + uint8_array.set_index(y as u32, x * 10 + y); + } + map.insert(x.to_string(), uint8_array); + } + map + }, + |map: HashMap| { + assert_eq!(map.len(), 10); + for (x, arr) in map.into_iter() { + assert_eq!(arr.length(), 10); + for y in 0..10 { + assert_eq!(arr.get_index(y as u32), (x.parse::().unwrap() * 10 + y)); + } + } + } +} + +// All the base-level types: +transferable_test! { + array_buffer, + js_sys::ArrayBuffer, + { + js_sys::ArrayBuffer::new(10) + }, + |arr: js_sys::ArrayBuffer| { + assert_eq!(arr.byte_length(), 10); + } +} +transferable_test! { + message_port, + web_sys::MessagePort, + { + let channel = web_sys::MessageChannel::new().unwrap(); + channel.port1() + }, + |port: web_sys::MessagePort| { + assert!(port.is_instance_of::()); + } +} +transferable_test! { + readable_stream, + web_sys::ReadableStream, + { + web_sys::Blob::new().unwrap().stream() + }, + |stream: web_sys::ReadableStream| { + assert!(stream.is_instance_of::()); + } +} +transferable_test! { + writable_stream, + web_sys::WritableStream, + { + web_sys::WritableStream::new().unwrap() + }, + |stream: web_sys::WritableStream| { + assert!(stream.is_instance_of::()); + } +} +transferable_test! { + transform_stream, + web_sys::TransformStream, + { + web_sys::TransformStream::new().unwrap() + }, + |stream: web_sys::TransformStream| { + assert!(stream.is_instance_of::()); + } +} +transferable_test! { + image_bitmap, + web_sys::ImageBitmap, + { + let jpg_data = include_bytes!("./assets/test_image.jpg"); + let arr = Uint8Array::new_with_length(jpg_data.len() as u32); + arr.copy_from(jpg_data.as_slice()); + let parts = js_sys::Array::new(); + parts.push(&arr.buffer()); + let image = web_sys::Blob::new_with_u8_array_sequence_and_options(&parts, web_sys::BlobPropertyBag::new().type_("image/jpeg")).unwrap(); + wasm_bindgen_futures::JsFuture::from( + web_sys::window().unwrap().create_image_bitmap_with_blob(&image).unwrap() + ).await.unwrap().unchecked_into::() + }, + |bitmap: web_sys::ImageBitmap| { + assert!(bitmap.is_instance_of::()); + } +} +transferable_test! { + offscreen_canvas, + web_sys::OffscreenCanvas, + { + web_sys::OffscreenCanvas::new(100, 100).unwrap() + }, + |canvas: web_sys::OffscreenCanvas| { + assert!(canvas.is_instance_of::()); + } +} + +// Only actually supported by safari currently: +// https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel#browser_compatibility +// transferable_test! { +// rtc_data_channel, +// web_sys::RtcDataChannel, +// { +// web_sys::RtcPeerConnection::new().unwrap().create_data_channel("test") +// }, +// |channel: web_sys::RtcDataChannel| { +// assert!(channel.is_instance_of::()); +// } +// } diff --git a/leptos_workers/tests/worker.rs b/leptos_workers/tests/worker.rs index 1154ade..1cab0a3 100644 --- a/leptos_workers/tests/worker.rs +++ b/leptos_workers/tests/worker.rs @@ -1,13 +1,8 @@ #![allow(clippy::self_assignment)] -use std::collections::HashMap; - -use futures::future::join_all; use gloo_timers::future::TimeoutFuture; -use js_sys::Uint8Array; use leptos_workers_macro::worker; use serde::{Deserialize, Serialize}; -use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen_test::*; wasm_bindgen_test_configure!(run_in_browser); @@ -243,325 +238,3 @@ async fn heavy_loop_is_interactive() { fn should_panic_on_ssr() { pollster::block_on(async { future_worker(TestRequest(5)).await.unwrap() }); } - -/// Seeded simple random byte generator fns. -fn seeded_rand_byte() -> impl FnMut() -> u8 { - let mut next = 0; - move || { - next = (next + 1) % 255; - next - } -} - -#[derive(Clone, Serialize, Deserialize)] -struct TransMsgArrWithCheck( - #[serde(with = "leptos_workers::transferable")] js_sys::Uint8Array, - Vec, -); - -impl TransMsgArrWithCheck { - pub fn check(&self) { - assert_eq!(self.0.length(), self.1.len() as u32); - for (i, byte) in self.1.iter().enumerate() { - assert_eq!(self.0.get_index(i as u32), *byte); - } - } -} - -#[worker] -async fn worker_contended(req: TransMsgArrWithCheck) -> TransMsgArrWithCheck { - req.check(); - req -} - -#[wasm_bindgen_test] -async fn test_transferable_whilst_contended() { - // Will concurrently call the worker this many times with varying requests: - let num_calls = 3; - - let mut get_next_byte = seeded_rand_byte(); - let mut reqs = Vec::with_capacity(num_calls); - for _ in 0..num_calls { - let mut vec = Vec::with_capacity(10); - for _ in 0..10 { - vec.push(get_next_byte()); - } - let js_arr = js_sys::Uint8Array::new_with_length(10); - js_arr.copy_from(&vec); - reqs.push(TransMsgArrWithCheck(js_arr, vec)); - } - // Process all via join simulating concurrency/contended serialization/deserialization: - for result in join_all(reqs.into_iter().map(worker_contended)).await { - result.unwrap().check(); - } -} - -/// Standard worker test, passing an object containing transferables to a worker and back again. -macro_rules! transferable_test { - ($id:ident, $typ:ty, $create:block, $verify:expr) => { - paste::item! { - #[allow(non_camel_case_types)] - #[derive(Clone, Serialize, Deserialize)] - struct [< TransMsg_ $id >](#[serde(with = "leptos_workers::transferable")] $typ); - - #[worker] - async fn [< worker_fn_ $id >](req: [< TransMsg_ $id >]) -> [< TransMsg_ $id >] { - ($verify)(req.0.clone()); - req - } - - #[wasm_bindgen_test] - async fn [< test_transferable_ $id >]() { - let contents = $create; - ($verify)(contents.clone()); - let response_msg = [< worker_fn_ $id >]([< TransMsg_ $id >](contents)) - .await - .unwrap(); - ($verify)(response_msg.0); - } - } - }; -} - -transferable_test! { - complex, - HashMap>>>, - { - let mut get_next_byte = seeded_rand_byte(); - let mut map = HashMap::new(); - for x in 0..10 { - let mut vec = Vec::new(); - for _y in 0..10 { - let mut inner_map = HashMap::new(); - for z in 0..10 { - let uint8_array = js_sys::Uint8Array::new(&js_sys::ArrayBuffer::new(10)); - for w in 0..10 { - uint8_array.set_index(w as u32, get_next_byte()); - } - inner_map.insert(z.to_string(), Some(uint8_array)); - } - vec.push(inner_map); - } - map.insert(x.to_string(), vec); - } - map - }, - |map: HashMap>>>| { - let mut get_next_byte = seeded_rand_byte(); - - assert_eq!(map.len(), 10); - for x in 0..10 { - let vec = map.get(&x.to_string()).unwrap(); - assert_eq!(vec.len(), 10); - for inner_map in vec.iter() { - assert_eq!(inner_map.len(), 10); - for z in 0..10 { - let uint8_array = inner_map.get(&z.to_string()).unwrap().as_ref().unwrap(); - assert_eq!(uint8_array.length(), 10); - for w in 0..10 { - assert_eq!(uint8_array.get_index(w as u32), get_next_byte()); - } - } - } - } - } -} - -transferable_test! { - uint8_array, - js_sys::Uint8Array, - { - let uint8_array = js_sys::Uint8Array::new(&js_sys::ArrayBuffer::new(10)); - for x in 0..10 { - uint8_array.set_index(x as u32, x); - } - uint8_array - }, - |arr: js_sys::Uint8Array| { - assert_eq!(arr.length(), 10); - for x in 0..10 { - assert_eq!(arr.get_index(x as u32), x); - } - } -} - -transferable_test! { - arb_js, - JsValue, - { - let js_val: JsValue = js_sys::ArrayBuffer::new(10).into(); - js_val - }, - |val: JsValue| { - let arr = val.dyn_into::().unwrap(); - assert_eq!(arr.byte_length(), 10); - } -} - -transferable_test! { - option_t_some, - Option, - { - Some(js_sys::ArrayBuffer::new(10)) - }, - |opt: Option| { - let arr = opt.unwrap(); - assert_eq!(arr.byte_length(), 10); - } -} - -transferable_test! { - option_t_none, - Option, - { - None - }, - |opt: Option| { - assert!(opt.is_none()); - } -} - -transferable_test! { - vec_of_t, - Vec, - { - // 10 items, each arr with 10 elements, all ordered by index: - let mut vec = Vec::new(); - for x in 0..10 { - let uint8_array = js_sys::Uint8Array::new(&js_sys::ArrayBuffer::new(10)); - for y in 0..10 { - uint8_array.set_index(y as u32, x * 10 + y); - } - vec.push(uint8_array); - } - vec - }, - |vec: Vec| { - assert_eq!(vec.len(), 10); - for (x, arr) in vec.into_iter().enumerate() { - assert_eq!(arr.length(), 10); - for y in 0..10 { - assert_eq!(arr.get_index(y as u32), (x * 10 + y) as u8); - } - } - } -} - -transferable_test! { - hashmap, - HashMap, - { - let mut map = HashMap::new(); - for x in 0..10 { - let uint8_array = js_sys::Uint8Array::new(&js_sys::ArrayBuffer::new(10)); - for y in 0..10 { - uint8_array.set_index(y as u32, x * 10 + y); - } - map.insert(x.to_string(), uint8_array); - } - map - }, - |map: HashMap| { - assert_eq!(map.len(), 10); - for (x, arr) in map.into_iter() { - assert_eq!(arr.length(), 10); - for y in 0..10 { - assert_eq!(arr.get_index(y as u32), (x.parse::().unwrap() * 10 + y)); - } - } - } -} - -// All the base-level types: -transferable_test! { - array_buffer, - js_sys::ArrayBuffer, - { - js_sys::ArrayBuffer::new(10) - }, - |arr: js_sys::ArrayBuffer| { - assert_eq!(arr.byte_length(), 10); - } -} -transferable_test! { - message_port, - web_sys::MessagePort, - { - let channel = web_sys::MessageChannel::new().unwrap(); - channel.port1() - }, - |port: web_sys::MessagePort| { - assert!(port.is_instance_of::()); - } -} -transferable_test! { - readable_stream, - web_sys::ReadableStream, - { - web_sys::Blob::new().unwrap().stream() - }, - |stream: web_sys::ReadableStream| { - assert!(stream.is_instance_of::()); - } -} -transferable_test! { - writable_stream, - web_sys::WritableStream, - { - web_sys::WritableStream::new().unwrap() - }, - |stream: web_sys::WritableStream| { - assert!(stream.is_instance_of::()); - } -} -transferable_test! { - transform_stream, - web_sys::TransformStream, - { - web_sys::TransformStream::new().unwrap() - }, - |stream: web_sys::TransformStream| { - assert!(stream.is_instance_of::()); - } -} -transferable_test! { - image_bitmap, - web_sys::ImageBitmap, - { - let jpg_data = include_bytes!("./assets/test_image.jpg"); - let arr = Uint8Array::new_with_length(jpg_data.len() as u32); - arr.copy_from(jpg_data.as_slice()); - let parts = js_sys::Array::new(); - parts.push(&arr.buffer()); - let image = web_sys::Blob::new_with_u8_array_sequence_and_options(&parts, web_sys::BlobPropertyBag::new().type_("image/jpeg")).unwrap(); - wasm_bindgen_futures::JsFuture::from( - web_sys::window().unwrap().create_image_bitmap_with_blob(&image).unwrap() - ).await.unwrap().unchecked_into::() - }, - |bitmap: web_sys::ImageBitmap| { - assert!(bitmap.is_instance_of::()); - } -} -transferable_test! { - offscreen_canvas, - web_sys::OffscreenCanvas, - { - web_sys::OffscreenCanvas::new(100, 100).unwrap() - }, - |canvas: web_sys::OffscreenCanvas| { - assert!(canvas.is_instance_of::()); - } -} - -// Only actually supported by safari currently: -// https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel#browser_compatibility -// transferable_test! { -// rtc_data_channel, -// web_sys::RtcDataChannel, -// { -// web_sys::RtcPeerConnection::new().unwrap().create_data_channel("test") -// }, -// |channel: web_sys::RtcDataChannel| { -// assert!(channel.is_instance_of::()); -// } -// } diff --git a/scripts/test.sh b/scripts/test.sh index 71a3ebd..0a2fada 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -5,7 +5,9 @@ set -e cargo fmt --all cargo fmt --all -- --check +cargo clippy --all-targets --workspace cargo clippy --all-targets --all-features --workspace +cargo test --workspace cargo test --all-features --workspace wasm-pack test --node leptos_workers -wasm-pack test --chrome leptos_workers +WASM_BINDGEN_TEST_TIMEOUT=100 wasm-pack test --firefox --headless leptos_workers