diff --git a/crates/cli/tests/reference.rs b/crates/cli/tests/reference.rs index 6a4c57956df..df9c7d27399 100644 --- a/crates/cli/tests/reference.rs +++ b/crates/cli/tests/reference.rs @@ -8,6 +8,27 @@ //! `*.js` files and `*.wat` files with the expected output of the `*.rs` //! compilation. Use `BLESS=1` in the environment to automatically update all //! tests. +//! +//! ## Dependencies +//! +//! By default, tests only have access to the `wasm-bindgen` and +//! `wasm-bindgen-futures` crates. Additional crates can be used by declaring +//! them as dependencies using a comment at the top of the test file. +//! For example: +//! +//! ```rust +//! // DEPENDENCY: web-sys = { path = '{root}/crates/web-sys', features = ['console', 'Url', 'MediaSourceReadyState'] } +//! ``` +//! +//! This will add the `web-sys` crate as a dependency to the test, allowing the +//! test to use the `console`, `Url`, and `MediaSourceReadyState` features, as +//! well as the `web-sys` crate itself. +//! +//! Note that the `{root}` placeholder will be replaced with the path to the +//! root of the `wasm-bindgen` repository. +//! +//! Multiple dependencies can be declared in a single test file using multiple +//! `DEPENDENCY:` comments. use anyhow::{bail, Result}; use assert_cmd::prelude::*; @@ -55,6 +76,15 @@ fn main() -> Result<()> { fn runtest(test: &Path) -> Result<()> { let contents = fs::read_to_string(test)?; let td = tempfile::TempDir::new()?; + let root = repo_root(); + let root = root.display(); + + // parse additional dependency declarations + let dependencies = contents + .lines() + .filter_map(|l| l.strip_prefix("// DEPENDENCY: ")) + .map(|l| "\n ".to_string() + &l.trim().replace("{root}", &root.to_string())) + .fold(String::new(), |a, b| a + &b); let manifest = format!( " @@ -65,16 +95,15 @@ fn runtest(test: &Path) -> Result<()> { edition = '2021' [dependencies] - wasm-bindgen = {{ path = '{}' }} - wasm-bindgen-futures = {{ path = '{}/crates/futures' }} + wasm-bindgen = {{ path = '{root}' }} + wasm-bindgen-futures = {{ path = '{root}/crates/futures' }} + {dependencies} [lib] crate-type = ['cdylib'] - path = '{}' + path = '{test}' ", - repo_root().display(), - repo_root().display(), - test.display(), + test = test.display(), ); fs::write(td.path().join("Cargo.toml"), manifest)?; diff --git a/crates/cli/tests/reference/web-sys.d.ts b/crates/cli/tests/reference/web-sys.d.ts new file mode 100644 index 00000000000..317ce71a92b --- /dev/null +++ b/crates/cli/tests/reference/web-sys.d.ts @@ -0,0 +1,10 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @returns {URL} + */ +export function get_url(): URL; +/** + * @returns {any} + */ +export function get_media_source(): any; diff --git a/crates/cli/tests/reference/web-sys.js b/crates/cli/tests/reference/web-sys.js new file mode 100644 index 00000000000..58229dc66a1 --- /dev/null +++ b/crates/cli/tests/reference/web-sys.js @@ -0,0 +1,235 @@ +let wasm; +export function __wbg_set_wasm(val) { + wasm = val; +} + + +const heap = new Array(128).fill(undefined); + +heap.push(undefined, null, true, false); + +function getObject(idx) { return heap[idx]; } + +function debugString(val) { + // primitive types + const type = typeof val; + if (type == 'number' || type == 'boolean' || val == null) { + return `${val}`; + } + if (type == 'string') { + return `"${val}"`; + } + if (type == 'symbol') { + const description = val.description; + if (description == null) { + return 'Symbol'; + } else { + return `Symbol(${description})`; + } + } + if (type == 'function') { + const name = val.name; + if (typeof name == 'string' && name.length > 0) { + return `Function(${name})`; + } else { + return 'Function'; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = '['; + if (length > 0) { + debug += debugString(val[0]); + } + for(let i = 1; i < length; i++) { + debug += ', ' + debugString(val[i]); + } + debug += ']'; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == 'Object') { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return 'Object(' + JSON.stringify(val) + ')'; + } catch (_) { + return 'Object'; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; +} + +let WASM_VECTOR_LEN = 0; + +let cachedUint8ArrayMemory0 = null; + +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +const lTextEncoder = typeof TextEncoder === 'undefined' ? (0, module.require)('util').TextEncoder : TextEncoder; + +let cachedTextEncoder = new lTextEncoder('utf-8'); + +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +} + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); + +function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +let cachedDataViewMemory0 = null; + +function getDataViewMemory0() { + if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) { + cachedDataViewMemory0 = new DataView(wasm.memory.buffer); + } + return cachedDataViewMemory0; +} + +const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; + +let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} +/** + * @returns {URL} + */ +export function get_url() { + const ret = wasm.get_url(); + return takeObject(ret); +} + +/** + * @returns {any} + */ +export function get_media_source() { + const ret = wasm.get_media_source(); + return __wbindgen_enum_MediaSourceEnum[ret]; +} + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); + } +} + +const __wbindgen_enum_MediaSourceEnum = ["camera", "screen", "application", "window", "browser", "microphone", "audioCapture", "other"]; + +const __wbindgen_enum_MediaSourceReadyState = ["closed", "open", "ended"]; + +export function __wbg_new_1cabf49927794f50() { return handleError(function (arg0, arg1) { + const ret = new URL(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); +}, arguments) }; + +export function __wbindgen_debug_string(arg0, arg1) { + const ret = debugString(getObject(arg1)); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true); + getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); +}; + +export function __wbindgen_throw(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +}; + +export function __wbindgen_object_drop_ref(arg0) { + takeObject(arg0); +}; + diff --git a/crates/cli/tests/reference/web-sys.rs b/crates/cli/tests/reference/web-sys.rs new file mode 100644 index 00000000000..6ad4315c2ed --- /dev/null +++ b/crates/cli/tests/reference/web-sys.rs @@ -0,0 +1,15 @@ +// DEPENDENCY: web-sys = { path = '{root}/crates/web-sys', features = ['console', 'Url', 'MediaSourceEnum', 'MediaSourceReadyState'] } + +use wasm_bindgen::prelude::wasm_bindgen; +use web_sys::{Url, MediaSourceEnum, MediaSourceReadyState}; + +#[wasm_bindgen] +pub fn get_url() -> Url { + assert_eq!(MediaSourceReadyState::Closed, MediaSourceReadyState::Closed); + Url::new("https://example.com").unwrap() +} + +#[wasm_bindgen] +pub fn get_media_source() -> MediaSourceEnum { + MediaSourceEnum::Camera +} diff --git a/crates/cli/tests/reference/web-sys.wat b/crates/cli/tests/reference/web-sys.wat new file mode 100644 index 00000000000..eefb7ad3e97 --- /dev/null +++ b/crates/cli/tests/reference/web-sys.wat @@ -0,0 +1,20 @@ +(module $reference_test.wasm + (type (;0;) (func (result i32))) + (type (;1;) (func (param i32))) + (type (;2;) (func (param i32 i32) (result i32))) + (type (;3;) (func (param i32 i32 i32 i32) (result i32))) + (func $__wbindgen_realloc (;0;) (type 3) (param i32 i32 i32 i32) (result i32)) + (func $__wbindgen_malloc (;1;) (type 2) (param i32 i32) (result i32)) + (func $get_url (;2;) (type 0) (result i32)) + (func $get_media_source (;3;) (type 0) (result i32)) + (func $__wbindgen_exn_store (;4;) (type 1) (param i32)) + (memory (;0;) 17) + (export "memory" (memory 0)) + (export "get_url" (func $get_url)) + (export "get_media_source" (func $get_media_source)) + (export "__wbindgen_malloc" (func $__wbindgen_malloc)) + (export "__wbindgen_realloc" (func $__wbindgen_realloc)) + (export "__wbindgen_exn_store" (func $__wbindgen_exn_store)) + (@custom "target_features" (after code) "\02+\0fmutable-globals+\08sign-ext") +) +