Repository: rustwasm/wasm-bindgen (rust)

Issue:
Let `#[wasm_bindgen_test]` resolve to just `#[test]` on non-wasm targets
### Motivation

When writing tests for libraries that target both `target_family = "wasm"` and native targets, I need to do this cfg dance:
```rs
#[cfg_attr(all(target_family = "wasm", target_os = "unknown"), wasm_bindgen_test)]
#[cfg_attr(not(all(target_family = "wasm", target_os = "unknown")), test)]
fn test_something() {
  // ...
}
```

### Proposed Solution

This should just work on native targets:
```rs
#[wasm_bindgen_test]
fn test_something() {
  // ...
}
```

It should have the exact same effect as
```rs
#[test]
fn test_something() {
  // ...
}
```

### Alternatives

What's written in `Motivation`. I'd love to reduce the amount of `cfg`ing I have to do, though.

### Additional Context

Perhaps it'd be useful to also support common alternative macros to substitute with for native code. E.g. to make async functions work more easily:
```rs
#[wasm_bindgen_test(tokio::test)] // resolves to #[tokio::test] on native targets
async fn test_something() {
  // ...
}
```


Pull request description:
Add `wasm_bindgen_test(unsupported = test)`
This allows tests to be run with user defined attributes on other targets then `wasm32-unknown-unknown`.
E.g. `#[wasm_bindgen_test(unsupported = tokio::test)]` would work as well for async functions.

Fixes #4139.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ca69427..8f8fecd3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,9 @@
 * Added support for `js_name` and `skip_typescript` attributes for string enums.
   [#4147](https://github.com/rustwasm/wasm-bindgen/pull/4147)
 
+* Added `unsupported` crate to `wasm_bindgen_test(unsupported = test)` as a way of running tests on non-Wasm targets as well.
+  [#4150](https://github.com/rustwasm/wasm-bindgen/pull/4150)
+
 ### Changed
 
 * Implicitly enable reference type and multivalue transformations if the module already makes use of the corresponding target features.
diff --git a/Cargo.toml b/Cargo.toml
index ac38d050..c5707b56 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -47,11 +47,13 @@ serde = { version = "1.0", optional = true }
 serde_json = { version = "1.0", optional = true }
 wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.93" }
 
+[dev-dependencies]
+wasm-bindgen-test = { path = 'crates/test' }
+
 [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
 js-sys = { path = 'crates/js-sys' }
 serde_derive = "1.0"
 wasm-bindgen-futures = { path = 'crates/futures' }
-wasm-bindgen-test = { path = 'crates/test' }
 wasm-bindgen-test-crate-a = { path = 'tests/crates/a' }
 wasm-bindgen-test-crate-b = { path = 'tests/crates/b' }
 


Test patch:
diff --git a/crates/test-macro/Cargo.toml b/crates/test-macro/Cargo.toml
index 87e55ad1..2c67d591 100644
--- a/crates/test-macro/Cargo.toml
+++ b/crates/test-macro/Cargo.toml
@@ -23,6 +23,7 @@ syn = { version = "2.0", default-features = false, features = [
 ] }
 
 [dev-dependencies]
+tokio = { version = "1", features = ["macros"] }
 trybuild = "1.0"
 wasm-bindgen-test = { path = "../test" }
 
diff --git a/crates/test-macro/src/lib.rs b/crates/test-macro/src/lib.rs
index d9bd7814..27f21988 100644
--- a/crates/test-macro/src/lib.rs
+++ b/crates/test-macro/src/lib.rs
@@ -120,6 +120,10 @@ pub fn wasm_bindgen_test(
         },
     );
 
+    if let Some(path) = attributes.unsupported {
+        tokens.extend(quote! { #[#path] });
+    }
+
     tokens.extend(leading_tokens);
     tokens.push(ident.into());
     tokens.extend(body);
@@ -281,6 +285,7 @@ fn compile_error(span: Span, msg: &str) -> proc_macro::TokenStream {
 struct Attributes {
     r#async: bool,
     wasm_bindgen_path: syn::Path,
+    unsupported: Option<syn::Meta>,
 }
 
 impl Default for Attributes {
@@ -288,6 +293,7 @@ impl Default for Attributes {
         Self {
             r#async: false,
             wasm_bindgen_path: syn::parse_quote!(::wasm_bindgen_test),
+            unsupported: None,
         }
     }
 }
@@ -298,6 +304,8 @@ impl Attributes {
             self.r#async = true;
         } else if meta.path.is_ident("crate") {
             self.wasm_bindgen_path = meta.value()?.parse::<syn::Path>()?;
+        } else if meta.path.is_ident("unsupported") {
+            self.unsupported = Some(meta.value()?.parse::<syn::Meta>()?);
         } else {
             return Err(meta.error("unknown attribute"));
         }
diff --git a/crates/test-macro/ui-tests/crate.rs b/crates/test-macro/ui-tests/crate.rs
index 2087f757..d7f0a33f 100644
--- a/crates/test-macro/ui-tests/crate.rs
+++ b/crates/test-macro/ui-tests/crate.rs
@@ -17,4 +17,7 @@ fn success_2() {}
 #[wasm_bindgen_test(crate = foo)]
 fn failure_1() {}
 
+#[wasm_bindgen_test(unsupported = tokio::test(flavor = "multi_thread", worker_threads = 1))]
+fn success_3() {}
+
 fn main() {}
diff --git a/guide/src/wasm-bindgen-test/usage.md b/guide/src/wasm-bindgen-test/usage.md
index 3ba7aa97..18912049 100644
--- a/guide/src/wasm-bindgen-test/usage.md
+++ b/guide/src/wasm-bindgen-test/usage.md
@@ -27,6 +27,13 @@ fn pass() {
 fn fail() {
     assert_eq!(1, 2);
 }
+
+// On a target other then `wasm32-unknown-unknown`, the `#[test]` attribute
+// will be used instead, allowing this test to run on any target.
+#[wasm_bindgen_test(unsupported = test)]
+fn all_targets() {
+    assert_eq!(1, 2);
+}
 ```
 
 Writing tests is the same as normal Rust `#[test]`s, except we are using the
diff --git a/tests/non_wasm_test.rs b/tests/non_wasm_test.rs
new file mode 100644
index 00000000..784fa8cf
--- /dev/null
+++ b/tests/non_wasm_test.rs
@@ -0,0 +1,25 @@
+#![cfg(not(target_family = "wasm"))]
+
+use std::sync::{Arc, Condvar, Mutex};
+
+use once_cell::sync::Lazy;
+use wasm_bindgen_test::wasm_bindgen_test;
+
+static TEST: Lazy<Arc<(Mutex<bool>, Condvar)>> =
+    Lazy::new(|| Arc::new((Mutex::new(false), Condvar::new())));
+
+#[wasm_bindgen_test(unsupported = test)]
+fn test_success() {
+    let mut success = TEST.0.lock().unwrap();
+    *success = true;
+    // We notify the condvar that the value has changed.
+    TEST.1.notify_one();
+}
+
+#[test]
+fn test() {
+    let mut success = TEST.0.lock().unwrap();
+    while !*success {
+        success = TEST.1.wait(success).unwrap();
+    }
+}