diff --git a/.gitignore b/.gitignore index e156cffa7..20dab02e2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ target/ **/target **/*.rs.bk **/pkg -tests/fixtures/**/Cargo.lock +tests/.crates.toml +tests/bin wasm-pack.log diff --git a/Cargo.lock b/Cargo.lock index 25fcc19d0..3cc7a58b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,8 @@ +[[package]] +name = "adler32" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "aho-corasick" version = "0.6.8" @@ -41,7 +46,7 @@ name = "backtrace-sys" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -50,14 +55,37 @@ name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bzip2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cc" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -131,6 +159,14 @@ dependencies = [ "walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "curl" version = "0.4.14" @@ -142,7 +178,7 @@ dependencies = [ "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.9.35 (registry+https://github.com/rust-lang/crates.io-index)", "schannel 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "socket2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -151,7 +187,7 @@ name = "curl-sys" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.9.35 (registry+https://github.com/rust-lang/crates.io-index)", @@ -197,6 +233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide_c_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -300,7 +337,7 @@ name = "libz-sys" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -328,10 +365,39 @@ name = "miniz-sys" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "miniz_oxide" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide_c_api" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "msdos_time" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-integer" version = "0.1.39" @@ -368,7 +434,7 @@ name = "openssl-src" version = "110.0.7+1.1.0i" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -376,7 +442,7 @@ name = "openssl-sys" version = "0.9.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-src 110.0.7+1.1.0i (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -424,6 +490,11 @@ name = "pkg-config" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "podio" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.4.15" @@ -594,7 +665,7 @@ dependencies = [ [[package]] name = "socket2" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -850,6 +921,7 @@ dependencies = [ "tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "which 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zip 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -906,15 +978,31 @@ dependencies = [ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "zip" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] +"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" -"checksum cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4a6007c146fdd28d4512a794b07ffe9d8e89e6bf86e2e0c4ddff2e1fb54a0007" +"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" +"checksum bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b" +"checksum cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "c37f0efaa4b9b001fa6f02d4b644dee4af97d3414df07c51e3e4f015f3a3e131" "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" @@ -922,6 +1010,7 @@ dependencies = [ "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum console 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7649ca90478264b9686aac8d269fcb014f14c2bed7c79a7e51b9f6afd4d783eb" "checksum copy_dir 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e4281031634644843bd2f5aa9c48cf98fc48d6b083bd90bb11becf10deaf8b0" +"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "checksum curl 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)" = "444c2f9e71458b34e75471ed8d756947a0bb920b8b8b9bfc56dfcc4fc6819a13" "checksum curl-sys 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "981bd902fcd8b8b999cf71b81447e27d66c3493a7f62f1372866fd32986c0c82" "checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9" @@ -944,6 +1033,9 @@ dependencies = [ "checksum lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "949826a5ccf18c1b3a7c3d57692778d21768b79e46eb9dd07bfc4c2160036c54" "checksum memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a3b4142ab8738a78c51896f704f83c11df047ff1bda9a92a661aa6361552d93d" "checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4" +"checksum miniz_oxide 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9ba430291c9d6cedae28bcd2d49d1c32fc57d60cd49086646c5dd5673a870eb5" +"checksum miniz_oxide_c_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5a5b8234d6103ebfba71e29786da4608540f862de5ce980a1c94f86a40ca0d51" +"checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" "checksum openssl 0.10.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6c24d3508b4fb6da175c10baac54c578b33f09c89ae90c6fe9788b3b4768efdc" @@ -955,6 +1047,7 @@ dependencies = [ "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" "checksum parking_lot_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06a2b6aae052309c2fd2161ef58f5067bc17bb758377a0de9d4b279d603fdd8a" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" +"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" "checksum proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "295af93acfb1d5be29c16ca5b3f82d863836efd9cb0c14fd83811eb9a110e452" "checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" "checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" @@ -976,7 +1069,7 @@ dependencies = [ "checksum slog-async 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e544d16c6b230d84c866662fe55e31aacfca6ae71e6fc49ae9a311cb379bfc2f" "checksum slog-term 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5951a808c40f419922ee014c15b6ae1cd34d963538b57d8a4778b9ca3fff1e0b" "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" -"checksum socket2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "962a516af4d3a7c272cb3a1d50a8cc4e5b41802e4ad54cfb7bee8ba61d37d703" +"checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8e9ad6a11096cbecdcca0cc6aa403fdfdbaeda2fb3323a39c98e6a166a1e45a" @@ -1014,3 +1107,4 @@ dependencies = [ "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" "checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +"checksum zip 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "36b9e08fb518a65cf7e08a1e482573eb87a2f4f8c6619316612a3c1f162fe822" diff --git a/Cargo.toml b/Cargo.toml index 77eac9a88..ac1d24fea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ structopt = "0.2" tar = "0.4.16" toml = "0.4" which = "2.0.0" +zip = "0.4.2" [dev-dependencies] copy_dir = "0.1.2" diff --git a/src/binaries.rs b/src/binaries.rs new file mode 100644 index 000000000..cb011fd71 --- /dev/null +++ b/src/binaries.rs @@ -0,0 +1,215 @@ +//! Utilities for finding and installing binaries that we depend on. + +use curl; +use error::Error; +use failure; +use flate2; +use slog::Logger; +use std::collections::HashSet; +use std::ffi; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; +use tar; +use which::which; +use zip; + +/// Get the local (at `$CRATE/bin/$BIN`; preferred) or global (on `$PATH`) path +/// for the given binary. +pub fn bin_path(log: &Logger, crate_path: &Path, bin: &str) -> Option { + assert!(!bin.ends_with(".exe")); + debug!(log, "Searching for {} binary...", bin); + + // Return the path to the local binary, if it exists. + let local_path = |crate_path: &Path| -> Option { + let mut p = crate_path.to_path_buf(); + p.push("bin"); + if cfg!(target_os = "windows") { + let mut bin = bin.to_string(); + bin.push_str(".exe"); + p.push(bin); + } else { + p.push(bin); + } + + debug!(log, "Checking for local {} binary at {}", bin, p.display()); + if p.is_file() { + Some(p) + } else { + None + } + }; + + // Return the path to the global binary, if it exists. + let global_path = || -> Option { + debug!(log, "Looking for global {} binary on $PATH", bin); + if let Ok(p) = which(bin) { + Some(p) + } else { + None + } + }; + + local_path(crate_path) + .or_else(global_path) + .map(|p| { + let p = p.canonicalize().unwrap_or(p); + debug!(log, "Using {} binary at {}", bin, p.display()); + p + }).or_else(|| { + debug!(log, "Could not find {} binary.", bin); + None + }) +} + +fn curl(url: &str) -> Result, failure::Error> { + let mut data = Vec::new(); + + fn with_url_context(url: &str, r: Result) -> Result + where + Result: failure::ResultExt, + { + use failure::ResultExt; + r.with_context(|_| format!("when requesting {}", url)) + } + + let mut easy = curl::easy::Easy::new(); + with_url_context(url, easy.follow_location(true))?; + with_url_context(url, easy.url(url))?; + + { + let mut transfer = easy.transfer(); + with_url_context( + url, + transfer.write_function(|part| { + data.extend_from_slice(part); + Ok(part.len()) + }), + )?; + with_url_context(url, transfer.perform())?; + } + + let code = with_url_context(url, easy.response_code())?; + if 200 <= code && code < 300 { + Ok(data) + } else { + Err(Error::http(&format!( + "received a bad HTTP status code ({}) when requesting {}", + code, url + )).into()) + } +} + +/// Download the `.tar.gz` file at the given URL and unpack the given binaries +/// from it into the given crate. +/// +/// Upon success, every `$BIN` in `binaries` will be at `$CRATE/bin/$BIN`. +pub fn install_binaries_from_targz_at_url<'a, I>( + crate_path: &Path, + url: &str, + binaries: I, +) -> Result<(), Error> +where + I: IntoIterator, +{ + let mut binaries: HashSet<_> = binaries.into_iter().map(ffi::OsStr::new).collect(); + + let tarball = curl(&url).map_err(|e| Error::http(&e.to_string()))?; + let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(&tarball[..])); + + let bin = crate_path.join("bin"); + fs::create_dir_all(&bin)?; + + for entry in archive.entries()? { + let mut entry = entry?; + + let dest = match entry.path()?.file_stem() { + Some(f) if binaries.contains(f) => { + binaries.remove(f); + bin.join(entry.path()?.file_name().unwrap()) + } + _ => continue, + }; + + entry.unpack(dest)?; + } + + if binaries.is_empty() { + Ok(()) + } else { + Err(Error::archive(&format!( + "the tarball at {} was missing expected executables: {}", + url, + binaries + .into_iter() + .map(|s| s.to_string_lossy()) + .collect::>() + .join(", "), + ))) + } +} + +/// Install binaries from within the given zip at the given URL. +/// +/// Upon success, the binaries will be at the `$CRATE/bin/$BIN` path. +pub fn install_binaries_from_zip_at_url<'a, I>( + crate_path: &Path, + url: &str, + binaries: I, +) -> Result<(), Error> +where + I: IntoIterator, +{ + let mut binaries: HashSet<_> = binaries.into_iter().map(ffi::OsStr::new).collect(); + + let data = curl(&url).map_err(|e| Error::http(&e.to_string()))?; + let data = io::Cursor::new(data); + let mut zip = zip::ZipArchive::new(data)?; + + let bin = crate_path.join("bin"); + fs::create_dir_all(&bin)?; + + for i in 0..zip.len() { + let mut entry = zip.by_index(i).unwrap(); + let entry_path = entry.sanitized_name(); + match entry_path.file_stem() { + Some(f) if binaries.contains(f) => { + binaries.remove(f); + let mut dest = bin_open_options() + .write(true) + .create_new(true) + .open(bin.join(entry_path.file_name().unwrap()))?; + io::copy(&mut entry, &mut dest)?; + } + _ => continue, + }; + } + + if binaries.is_empty() { + Ok(()) + } else { + Err(Error::archive(&format!( + "the zip at {} was missing expected executables: {}", + url, + binaries + .into_iter() + .map(|s| s.to_string_lossy()) + .collect::>() + .join(", "), + ))) + } +} + +#[cfg(unix)] +fn bin_open_options() -> fs::OpenOptions { + use std::os::unix::fs::OpenOptionsExt; + + let mut opts = fs::OpenOptions::new(); + opts.mode(0o755); + opts +} + +#[cfg(not(unix))] +fn bin_open_options() -> fs::OpenOptions { + fs::OpenOptions::new() +} diff --git a/src/bindgen.rs b/src/bindgen.rs index 4166aa215..1b29deb04 100644 --- a/src/bindgen.rs +++ b/src/bindgen.rs @@ -1,18 +1,13 @@ //! Functionality related to installing and running `wasm-bindgen`. -use curl; +use binaries::{bin_path, install_binaries_from_targz_at_url}; use emoji; use error::Error; -use failure; -use flate2; use progressbar::Step; use slog::Logger; -use std::ffi; -use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; -use tar; -use which::which; +use target; use PBAR; /// Install the `wasm-bindgen` CLI. @@ -29,8 +24,8 @@ pub fn install_wasm_bindgen( log: &Logger, ) -> Result<(), Error> { // If the `wasm-bindgen` dependency is already met, print a message and return. - if wasm_bindgen_path(root_path) - .map(|bindgen_path| wasm_bindgen_version_check(&bindgen_path, version)) + if wasm_bindgen_path(log, root_path) + .map(|bindgen_path| wasm_bindgen_version_check(&bindgen_path, version, log)) .unwrap_or(false) { let msg = format!("{}wasm-bindgen already installed...", emoji::DOWN_ARROW); @@ -57,53 +52,11 @@ pub fn install_wasm_bindgen( }) } -fn curl(url: &str) -> Result, failure::Error> { - let mut data = Vec::new(); - - fn with_url_context(url: &str, r: Result) -> Result - where - Result: failure::ResultExt, - { - use failure::ResultExt; - r.with_context(|_| format!("when requesting {}", url)) - } - - let mut easy = curl::easy::Easy::new(); - with_url_context(url, easy.follow_location(true))?; - with_url_context(url, easy.url(url))?; - - { - let mut transfer = easy.transfer(); - with_url_context( - url, - transfer.write_function(|part| { - data.extend_from_slice(part); - Ok(part.len()) - }), - )?; - with_url_context(url, transfer.perform())?; - } - - let code = with_url_context(url, easy.response_code())?; - if 200 <= code && code < 300 { - Ok(data) - } else { - Err(Error::http(&format!( - "received a bad HTTP status code ({}) when requesting {}", - code, url - )).into()) - } -} - /// Download a tarball containing a pre-built `wasm-bindgen` binary. pub fn download_prebuilt_wasm_bindgen(root_path: &Path, version: &str) -> Result<(), Error> { - let linux = cfg!(target_os = "linux"); - let macos = cfg!(target_os = "macos"); - let x86_64 = cfg!(target_arch = "x86_64"); - - let target = if linux && x86_64 { + let target = if target::LINUX && target::x86_64 { "x86_64-unknown-linux-musl" - } else if macos && x86_64 { + } else if target::MACOS && target::x86_64 { "x86_64-apple-darwin" } else { return Err(Error::unsupported( @@ -117,40 +70,11 @@ pub fn download_prebuilt_wasm_bindgen(root_path: &Path, version: &str) -> Result target ); - let tarball = curl(&url).map_err(|e| Error::http(&e.to_string()))?; - let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(&tarball[..])); - - let bin = root_path.join("bin"); - fs::create_dir_all(&bin)?; - - let mut found_wasm_bindgen = false; - let mut found_test_runner = false; - - for entry in archive.entries()? { - let mut entry = entry?; - - let dest = match entry.path()?.file_stem() { - Some(f) if f == ffi::OsStr::new("wasm-bindgen") => { - found_wasm_bindgen = true; - bin.join(entry.path()?.file_name().unwrap()) - } - Some(f) if f == ffi::OsStr::new("wasm-bindgen-test-runner") => { - found_test_runner = true; - bin.join(entry.path()?.file_name().unwrap()) - } - _ => continue, - }; - - entry.unpack(dest)?; - } - - if found_wasm_bindgen && found_test_runner { - Ok(()) - } else { - Err(Error::archive( - "the wasm-bindgen tarball was missing expected executables", - )) - } + install_binaries_from_targz_at_url( + root_path, + &url, + vec!["wasm-bindgen", "wasm-bindgen-test-runner"], + ) } /// Use `cargo install` to install the `wasm-bindgen` CLI to the given root @@ -191,6 +115,7 @@ pub fn wasm_bindgen_build( target: &str, debug: bool, step: &Step, + log: &Logger, ) -> Result<(), Error> { let msg = format!("{}Running WASM-bindgen...", emoji::RUNNER); PBAR.step(step, &msg); @@ -198,7 +123,7 @@ pub fn wasm_bindgen_build( let binary_name = name.replace("-", "_"); let release_or_debug = if debug { "debug" } else { "release" }; - if let Some(wasm_bindgen_path) = wasm_bindgen_path(path) { + if let Some(wasm_bindgen_path) = wasm_bindgen_path(log, path) { let wasm_path = format!( "target/wasm32-unknown-unknown/{}/{}.wasm", release_or_debug, binary_name @@ -233,7 +158,7 @@ pub fn wasm_bindgen_build( } /// Check if the `wasm-bindgen` dependency is locally satisfied. -fn wasm_bindgen_version_check(bindgen_path: &PathBuf, dep_version: &str) -> bool { +fn wasm_bindgen_version_check(bindgen_path: &PathBuf, dep_version: &str, log: &Logger) -> bool { Command::new(bindgen_path) .arg("--version") .output() @@ -244,38 +169,27 @@ fn wasm_bindgen_version_check(bindgen_path: &PathBuf, dep_version: &str) -> bool .trim() .split_whitespace() .nth(1) - .map(|v| v == dep_version) - .unwrap_or(false) + .map(|v| { + info!( + log, + "Checking installed `wasm-bindgen` version == expected version: {} == {}", + v, + dep_version + ); + v == dep_version + }).unwrap_or(false) }).unwrap_or(false) } /// Return a `PathBuf` containing the path to either the local wasm-bindgen /// version, or the globally installed version if there is no local version. -fn wasm_bindgen_path(crate_path: &Path) -> Option { - // Return the path to the local `wasm-bindgen`, if it exists. - let local_bindgen_path = |crate_path: &Path| -> Option { - let mut p = crate_path.to_path_buf(); - p.push("bin"); - if cfg!(target_os = "windows") { - p.push("wasm-bindgen.exe"); - } else { - p.push("wasm-bindgen"); - } - if p.is_file() { - Some(p) - } else { - None - } - }; - - // Return the path to the global `wasm-bindgen`, if it exists. - let global_bindgen_path = || -> Option { - if let Ok(p) = which("wasm-bindgen") { - Some(p) - } else { - None - } - }; +fn wasm_bindgen_path(log: &Logger, crate_path: &Path) -> Option { + bin_path(log, crate_path, "wasm-bindgen") +} - local_bindgen_path(crate_path).or_else(global_bindgen_path) +/// Return a `PathBuf` containing the path to either the local +/// wasm-bindgen-test-runner version, or the globally installed version if there +/// is no local version. +pub fn wasm_bindgen_test_runner_path(log: &Logger, crate_path: &Path) -> Option { + bin_path(log, crate_path, "wasm-bindgen-test-runner") } diff --git a/src/build.rs b/src/build.rs index c568d911b..6200c595b 100644 --- a/src/build.rs +++ b/src/build.rs @@ -67,3 +67,27 @@ pub fn cargo_build_wasm(path: &Path, debug: bool, step: &Step) -> Result<(), Err Ok(()) } } + +/// Run `cargo build --tests` with the `nightly` toolchain and targetting +/// `wasm32-unknown-unknown`. +pub fn cargo_build_wasm_tests(path: &Path, debug: bool) -> Result<(), Error> { + let output = { + let mut cmd = Command::new("cargo"); + cmd.current_dir(path) + .arg("+nightly") + .arg("build") + .arg("--tests"); + if !debug { + cmd.arg("--release"); + } + cmd.arg("--target").arg("wasm32-unknown-unknown"); + cmd.output()? + }; + + if !output.status.success() { + let s = String::from_utf8_lossy(&output.stderr); + Error::cli("Compilation of your program failed", s) + } else { + Ok(()) + } +} diff --git a/src/command/build.rs b/src/command/build.rs index 343d77221..76d0747a3 100644 --- a/src/command/build.rs +++ b/src/command/build.rs @@ -1,3 +1,5 @@ +//! Implementation of the `wasm-pack build` command. + use bindgen; use build; use command::utils::{create_pkg_dir, set_crate_path}; @@ -9,6 +11,7 @@ use progressbar::Step; use readme; use slog::Logger; use std::path::PathBuf; +use std::str::FromStr; use std::time::Instant; use PBAR; @@ -26,6 +29,7 @@ pub(crate) struct Build { /// The `BuildMode` determines which mode of initialization we are running, and /// what build and install steps we perform. +#[derive(Clone, Copy, Debug)] pub enum BuildMode { /// Perform all the build and install steps. Normal, @@ -34,6 +38,23 @@ pub enum BuildMode { Noinstall, } +impl Default for BuildMode { + fn default() -> BuildMode { + BuildMode::Normal + } +} + +impl FromStr for BuildMode { + type Err = Error; + fn from_str(s: &str) -> Result { + match s { + "no-install" => Ok(BuildMode::Noinstall), + "normal" => Ok(BuildMode::Normal), + _ => Error::crate_config(&format!("Unknown build mode: {}", s)).map(|_| unreachable!()), + } + } +} + /// Everything required to configure and run the `wasm-pack build` command. #[derive(Debug, StructOpt)] pub struct BuildOptions { @@ -47,7 +68,7 @@ pub struct BuildOptions { #[structopt(long = "mode", short = "m", default_value = "normal")] /// Sets steps to be run. [possible values: no-install, normal] - pub mode: String, + pub mode: BuildMode, #[structopt(long = "no-typescript")] /// By default a *.d.ts file is generated for the generated JS file, but @@ -70,12 +91,8 @@ type BuildStep = fn(&mut Build, &Step, &Logger) -> Result<(), Error>; impl Build { /// Construct a build command from the given options. pub fn try_from_opts(build_opts: BuildOptions) -> Result { - let crate_path = set_crate_path(build_opts.path); + let crate_path = set_crate_path(build_opts.path)?; let crate_name = manifest::get_crate_name(&crate_path)?; - let mode = match build_opts.mode.as_str() { - "no-install" => BuildMode::Noinstall, - _ => BuildMode::Normal, - }; // let build_config = manifest::xxx(&crate_path).xxx(); Ok(Build { crate_path, @@ -83,7 +100,7 @@ impl Build { disable_dts: build_opts.disable_dts, target: build_opts.target, debug: build_opts.debug, - mode, + mode: build_opts.mode, // build_config, crate_name, }) @@ -255,6 +272,7 @@ impl Build { &self.target, self.debug, step, + log, )?; info!( &log, diff --git a/src/command/mod.rs b/src/command/mod.rs index 135e00961..a47df3ffd 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -1,15 +1,17 @@ //! CLI command structures, parsing, and execution. -mod build; +pub mod build; mod login; mod pack; mod publish; +pub mod test; pub mod utils; use self::build::{Build, BuildOptions}; use self::login::login; use self::pack::pack; use self::publish::publish; +use self::test::{Test, TestOptions}; use error::Error; use slog::Logger; use std::path::PathBuf; @@ -70,6 +72,10 @@ pub enum Command { /// strategies besides classic username/password entry in legacy npm. auth_type: Option, }, + + #[structopt(name = "test")] + /// 👩‍🔬 test your wasm! + Test(TestOptions), } /// Run a command with the given logger! @@ -108,6 +114,10 @@ pub fn run_wasm_pack(command: Command, log: &Logger) -> result::Result<(), Error ); login(registry, scope, always_auth, auth_type, &log) } + Command::Test(test_opts) => { + info!(&log, "Running test command..."); + Test::try_from_opts(test_opts).and_then(|t| t.run(&log)) + } }; match status { diff --git a/src/command/pack.rs b/src/command/pack.rs index fe1ca5ff2..5390b5790 100644 --- a/src/command/pack.rs +++ b/src/command/pack.rs @@ -9,7 +9,7 @@ use PBAR; /// Executes the 'npm pack' command on the 'pkg' directory /// which creates a tarball that can be published to the NPM registry pub fn pack(path: Option, log: &Logger) -> result::Result<(), Error> { - let crate_path = set_crate_path(path); + let crate_path = set_crate_path(path)?; info!(&log, "Packing up the npm package..."); let pkg_directory = find_pkg_directory(&crate_path).ok_or(Error::PkgNotFound { diff --git a/src/command/publish.rs b/src/command/publish.rs index 2d67365d1..1c9f33575 100644 --- a/src/command/publish.rs +++ b/src/command/publish.rs @@ -9,7 +9,7 @@ use PBAR; /// Creates a tarball from a 'pkg' directory /// and publishes it to the NPM registry pub fn publish(path: Option, log: &Logger) -> result::Result<(), Error> { - let crate_path = set_crate_path(path); + let crate_path = set_crate_path(path)?; info!(&log, "Publishing the npm package..."); info!(&log, "npm info located in the npm debug log"); diff --git a/src/command/test.rs b/src/command/test.rs new file mode 100644 index 000000000..2acd08e35 --- /dev/null +++ b/src/command/test.rs @@ -0,0 +1,404 @@ +//! Implementation of the `wasm-pack test` command. + +use super::build::BuildMode; +use bindgen; +use build; +use command::utils::set_crate_path; +use emoji; +use error::Error; +use indicatif::HumanDuration; +use manifest; +use progressbar::Step; +use slog::Logger; +use std::path::PathBuf; +use std::time::Instant; +use test; +use webdriver; +use PBAR; + +#[derive(Debug, Default, StructOpt)] +/// Everything required to configure the `wasm-pack test` command. +pub struct TestOptions { + #[structopt(parse(from_os_str))] + /// The path to the Rust crate. + pub path: Option, + + #[structopt(long = "node")] + /// Run the tests in Node.js. + pub node: bool, + + #[structopt(long = "firefox")] + /// Run the tests in Firefox. This machine must have a Firefox installation. + /// If the `geckodriver` WebDriver client is not on the `$PATH`, and not + /// specified with `--geckodriver`, then `wasm-pack` will download a local + /// copy. + pub firefox: bool, + + #[structopt(long = "geckodriver", parse(from_os_str))] + /// The path to the `geckodriver` WebDriver client for testing in + /// Firefox. Implies `--firefox`. + pub geckodriver: Option, + + #[structopt(long = "chrome")] + /// Run the tests in Chrome. This machine must have a Chrome installation. + /// If the `chromedriver` WebDriver client is not on the `$PATH`, and not + /// specified with `--chromedriver`, then `wasm-pack` will download a local + /// copy. + pub chrome: bool, + + #[structopt(long = "chromedriver", parse(from_os_str))] + /// The path to the `chromedriver` WebDriver client for testing in + /// Chrome. Implies `--chrome`. + pub chromedriver: Option, + + #[structopt(long = "safari")] + /// Run the tests in Safari. This machine must have a Safari installation, + /// and the `safaridriver` WebDriver client must either be on the `$PATH` or + /// specified explicitly with the `--safaridriver` flag. `wasm-pack` cannot + /// download the `safaridriver` WebDriver client for you. + pub safari: bool, + + #[structopt(long = "safaridriver", parse(from_os_str))] + /// The path to the `safaridriver` WebDriver client for testing in + /// Safari. Implies `--safari`. + pub safaridriver: Option, + + #[structopt(long = "headless")] + /// When running browser tests, run the browser in headless mode without any + /// UI or windows. + pub headless: bool, + + #[structopt(long = "mode", short = "m", default_value = "normal")] + /// Sets steps to be run. [possible values: no-install, normal] + pub mode: BuildMode, + + #[structopt(long = "release", short = "r")] + /// Build with the release profile. + pub release: bool, +} + +/// A configured `wasm-pack test` command. +pub struct Test { + crate_path: PathBuf, + node: bool, + mode: BuildMode, + firefox: bool, + geckodriver: Option, + chrome: bool, + chromedriver: Option, + safari: bool, + safaridriver: Option, + headless: bool, + release: bool, + test_runner_path: Option, +} + +type TestStep = fn(&mut Test, &Step, &Logger) -> Result<(), Error>; + +impl Test { + /// Construct a test command from the given options. + pub fn try_from_opts(test_opts: TestOptions) -> Result { + let TestOptions { + path, + node, + mode, + headless, + release, + chrome, + chromedriver, + firefox, + geckodriver, + safari, + safaridriver, + } = test_opts; + + let crate_path = set_crate_path(path)?; + + // let geckodriver = get_web_driver("geckodriver", test_opts.geckodriver, test_opts.firefox)?; + // let chromedriver = + // get_web_driver("chromedriver", test_opts.chromedriver, test_opts.chrome)?; + // let safaridriver = + // get_web_driver("safaridriver", test_opts.safaridriver, test_opts.safari)?; + + let any_browser = chrome || firefox || safari; + + if !node && !any_browser { + return Error::crate_config( + "Must specify at least one of `--node`, `--chrome`, `--firefox`, or `--safari`", + ).map(|_| unreachable!()); + } + + if headless && !any_browser { + return Error::crate_config("The `--headless` flag only applies to browser tests") + .map(|_| unreachable!()); + } + + Ok(Test { + crate_path, + node, + mode, + chrome, + chromedriver, + firefox, + geckodriver, + safari, + safaridriver, + headless, + release, + test_runner_path: None, + }) + } + + /// Execute this test command. + pub fn run(mut self, log: &Logger) -> Result<(), Error> { + let process_steps = self.get_process_steps(); + let mut step_counter = Step::new(process_steps.len()); + + let started = Instant::now(); + for (_, process_step) in process_steps { + process_step(&mut self, &step_counter, log)?; + step_counter.inc(); + } + let duration = HumanDuration(started.elapsed()); + info!(&log, "Done in {}.", &duration); + + Ok(()) + } + + fn get_process_steps(&self) -> Vec<(&'static str, TestStep)> { + macro_rules! steps { + ($($name:ident $(if $e:expr)* ),+) => { + { + let mut steps: Vec<(&'static str, TestStep)> = Vec::new(); + $( + $(if $e)* { + steps.push((stringify!($name), Test::$name)); + } + )* + steps + } + }; + ($($name:ident $(if $e:expr)* ,)*) => (steps![$($name $(if $e)* ),*]) + } + match self.mode { + BuildMode::Normal => steps![ + step_check_crate_config, + step_add_wasm_target, + step_build_tests, + step_install_wasm_bindgen, + step_test_node if self.node, + step_get_chromedriver if self.chrome && self.chromedriver.is_none(), + step_test_chrome if self.chrome, + step_get_geckodriver if self.firefox && self.geckodriver.is_none(), + step_test_firefox if self.firefox, + step_get_safaridriver if self.safari && self.safaridriver.is_none(), + step_test_safari if self.safari, + ], + BuildMode::Noinstall => steps![ + step_check_crate_config, + step_build_tests, + step_install_wasm_bindgen, + step_test_node if self.node, + step_get_chromedriver if self.chrome && self.chromedriver.is_none(), + step_test_chrome if self.chrome, + step_get_geckodriver if self.firefox && self.geckodriver.is_none(), + step_test_firefox if self.firefox, + step_get_safaridriver if self.safari && self.safaridriver.is_none(), + step_test_safari if self.safari, + ], + } + } + + fn step_check_crate_config(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + info!(log, "Checking crate configuration..."); + manifest::check_crate_config(&self.crate_path, step)?; + info!(log, "Crate is correctly configured."); + Ok(()) + } + + fn step_add_wasm_target(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + info!(&log, "Adding wasm-target..."); + build::rustup_add_wasm_target(step)?; + info!(&log, "Adding wasm-target was successful."); + Ok(()) + } + + fn step_build_tests(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + info!(log, "Compiling tests to wasm..."); + + let msg = format!("{}Compiling tests to WASM...", emoji::CYCLONE); + PBAR.step(step, &msg); + + build::cargo_build_wasm_tests(&self.crate_path, !self.release)?; + + info!(log, "Finished compiling tests to wasm."); + Ok(()) + } + + fn step_install_wasm_bindgen(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + info!(&log, "Identifying wasm-bindgen dependency..."); + let bindgen_version = manifest::get_wasm_bindgen_version(&self.crate_path)?; + info!(&log, "Installing wasm-bindgen-cli..."); + + let install_permitted = match self.mode { + BuildMode::Normal => true, + BuildMode::Noinstall => false, + }; + + bindgen::install_wasm_bindgen( + &self.crate_path, + &bindgen_version, + install_permitted, + step, + log, + )?; + + self.test_runner_path = Some(bindgen::wasm_bindgen_test_runner_path(log, &self.crate_path) + .expect("if installing wasm-bindgen succeeded, then we should have wasm-bindgen-test-runner too")); + + info!(&log, "Installing wasm-bindgen-cli was successful."); + Ok(()) + } + + fn step_test_node(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + assert!(self.node); + info!(log, "Running tests in node..."); + PBAR.step(step, "Running tests in node..."); + test::cargo_test_wasm( + &self.crate_path, + self.release, + log, + Some(( + "CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER", + &self.test_runner_path.as_ref().unwrap(), + )), + )?; + info!(log, "Finished running tests in node."); + Ok(()) + } + + fn step_get_chromedriver(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + PBAR.step(step, "Getting chromedriver..."); + assert!(self.chrome && self.chromedriver.is_none()); + + self.chromedriver = Some(webdriver::get_or_install_chromedriver( + log, + &self.crate_path, + self.mode, + )?); + Ok(()) + } + + fn step_test_chrome(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + PBAR.step(step, "Running tests in Chrome..."); + + let chromedriver = self.chromedriver.as_ref().unwrap().display().to_string(); + let chromedriver = chromedriver.as_str(); + info!( + log, + "Running tests in Chrome with chromedriver at {}", chromedriver + ); + + let test_runner = self + .test_runner_path + .as_ref() + .unwrap() + .display() + .to_string(); + let test_runner = test_runner.as_str(); + info!(log, "Using wasm-bindgen test runner at {}", test_runner); + + let mut envs = vec![ + ("CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER", test_runner), + ("CHROMEDRIVER", chromedriver), + ]; + if !self.headless { + envs.push(("NO_HEADLESS", "1")); + } + + test::cargo_test_wasm(&self.crate_path, self.release, log, envs) + } + + fn step_get_geckodriver(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + PBAR.step(step, "Getting geckodriver..."); + assert!(self.firefox && self.geckodriver.is_none()); + + self.geckodriver = Some(webdriver::get_or_install_geckodriver( + log, + &self.crate_path, + self.mode, + )?); + Ok(()) + } + + fn step_test_firefox(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + PBAR.step(step, "Running tests in Firefox..."); + + let geckodriver = self.geckodriver.as_ref().unwrap().display().to_string(); + let geckodriver = geckodriver.as_str(); + info!( + log, + "Running tests in Firefox with geckodriver at {}", geckodriver + ); + + let test_runner = self + .test_runner_path + .as_ref() + .unwrap() + .display() + .to_string(); + let test_runner = test_runner.as_str(); + info!(log, "Using wasm-bindgen test runner at {}", test_runner); + + let mut envs = vec![ + ("CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER", test_runner), + ("GECKODRIVER", geckodriver), + ]; + if !self.headless { + envs.push(("NO_HEADLESS", "1")); + } + + test::cargo_test_wasm(&self.crate_path, self.release, log, envs) + } + + fn step_get_safaridriver(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + PBAR.step(step, "Getting safaridriver..."); + assert!(self.safari && self.safaridriver.is_none()); + + self.safaridriver = Some(webdriver::get_safaridriver( + log, + &self.crate_path, + )?); + Ok(()) + } + + fn step_test_safari(&mut self, step: &Step, log: &Logger) -> Result<(), Error> { + PBAR.step(step, "Running tests in Safari..."); + + let safaridriver = self.safaridriver.as_ref().unwrap().display().to_string(); + let safaridriver = safaridriver.as_str(); + info!( + log, + "Running tests in Safari with safaridriver at {}", safaridriver + ); + + let test_runner = self + .test_runner_path + .as_ref() + .unwrap() + .display() + .to_string(); + let test_runner = test_runner.as_str(); + info!(log, "Using wasm-bindgen test runner at {}", test_runner); + + let mut envs = vec![ + ("CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER", test_runner), + ("SAFARIDRIVER", safaridriver), + ]; + if !self.headless { + envs.push(("NO_HEADLESS", "1")); + } + + test::cargo_test_wasm(&self.crate_path, self.release, log, envs) + } +} diff --git a/src/command/utils.rs b/src/command/utils.rs index c66b089e7..db4da5b8e 100644 --- a/src/command/utils.rs +++ b/src/command/utils.rs @@ -4,18 +4,19 @@ use emoji; use error::Error; use progressbar::Step; use std::fs; +use std::io; use std::path::{Path, PathBuf}; use PBAR; /// If an explicit path is given, then use it, otherwise assume the current /// directory is the crate path. -pub fn set_crate_path(path: Option) -> PathBuf { +pub fn set_crate_path(path: Option) -> io::Result { let crate_path = match path { Some(p) => p, None => PathBuf::from("."), }; - crate_path + crate_path.canonicalize() } /// Construct our `pkg` directory in the crate. diff --git a/src/error.rs b/src/error.rs index 597211f53..36463980a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,7 @@ use serde_json; use std::borrow::Cow; use std::io; use toml; +use zip; /// Errors that can potentially occur in `wasm-pack`. #[derive(Debug, Fail)] @@ -24,6 +25,10 @@ pub enum Error { /// A curl error. Curl(#[cause] curl::Error), + #[fail(display = "{}", _0)] + /// An error handling zip archives. + Zip(#[cause] zip::result::ZipError), + /// An error invoking another CLI tool. #[fail(display = "{}. stderr:\n\n{}", message, stderr)] Cli { @@ -113,6 +118,7 @@ impl Error { Error::Io(_) => "There was an I/O error. Details:\n\n", Error::SerdeJson(_) => "There was an JSON error. Details:\n\n", Error::SerdeToml(_) => "There was an TOML error. Details:\n\n", + Error::Zip(_) => "There was an error handling zip files. Details:\n\n", Error::Cli { message: _, stderr: _, @@ -149,6 +155,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: zip::result::ZipError) -> Self { + Error::Zip(e) + } +} + impl From for Error { fn from(e: toml::de::Error) -> Self { Error::SerdeToml(e) diff --git a/src/lib.rs b/src/lib.rs index 5873494c9..bc1e52282 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,9 @@ extern crate slog_term; extern crate tar; extern crate toml; extern crate which; +extern crate zip; +pub mod binaries; pub mod bindgen; pub mod build; pub mod command; @@ -34,6 +36,9 @@ pub mod manifest; pub mod npm; pub mod progressbar; pub mod readme; +pub mod target; +pub mod test; +pub mod webdriver; use progressbar::ProgressOutput; diff --git a/src/logger.rs b/src/logger.rs index 0f272a963..3e6ef83d1 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -39,6 +39,7 @@ fn log_file_path(cmd: &Command) -> PathBuf { Command::Build(build_opts) => &build_opts.path, Command::Pack { path } => path, Command::Publish { path } => path, + Command::Test(test_opts) => &test_opts.path, Command::Login { .. } => &None, }; diff --git a/src/manifest.rs b/src/manifest.rs index 70c92fa03..0c7b7c63a 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -13,14 +13,14 @@ use serde_json; use toml; use PBAR; -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] struct CargoManifest { package: CargoPackage, dependencies: Option>, lib: Option, } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] struct CargoPackage { name: String, authors: Vec, @@ -30,19 +30,19 @@ struct CargoPackage { repository: Option, } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] #[serde(untagged)] enum CargoDependency { Simple(String), Detailed(DetailedCargoDependency), } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] struct DetailedCargoDependency { version: Option, } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] struct CargoLib { #[serde(rename = "crate-type")] crate_type: Option>, @@ -191,6 +191,8 @@ pub fn check_crate_config(path: &Path, step: &Step) -> Result<(), Error> { let msg = format!("{}Checking crate configuration...", emoji::WRENCH); PBAR.step(&step, &msg); check_wasm_bindgen(path)?; + // TODO: check that if there is a `wasm-bindgen-test` dependency, then it + // matches `wasm-bindgen`. check_crate_type(path)?; Ok(()) } diff --git a/src/target.rs b/src/target.rs new file mode 100644 index 000000000..0d8179d9b --- /dev/null +++ b/src/target.rs @@ -0,0 +1,12 @@ +//! Information about the target. + +#![allow(missing_docs)] + +pub const LINUX: bool = cfg!(target_os = "linux"); +pub const MACOS: bool = cfg!(target_os = "macos"); +pub const WINDOWS: bool = cfg!(target_os = "windows"); + +#[allow(non_upper_case_globals)] +pub const x86_64: bool = cfg!(target_arch = "x86_64"); +#[allow(non_upper_case_globals)] +pub const x86: bool = cfg!(target_arch = "x86"); diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 000000000..d432b4e9f --- /dev/null +++ b/src/test.rs @@ -0,0 +1,49 @@ +//! Testing a Rust crate compiled to wasm. + +use error::Error; +use slog::Logger; +use std::ffi::OsStr; +use std::path::Path; +use std::process::Command; + +/// Run `cargo test` with the `nightly` toolchain and targeting +/// `wasm32-unknown-unknown`. +pub fn cargo_test_wasm( + path: &Path, + release: bool, + log: &Logger, + envs: I, +) -> Result<(), Error> +where + I: IntoIterator, + K: AsRef, + V: AsRef, +{ + use std::sync::Mutex; + lazy_static! { + static ref ONE_TEST_AT_A_TIME: Mutex<()> = Mutex::new(()); + } + let _locked = ONE_TEST_AT_A_TIME.lock().unwrap(); + + let output = { + let mut cmd = Command::new("cargo"); + cmd.envs(envs); + cmd.current_dir(path).arg("+nightly").arg("test"); + if release { + cmd.arg("--release"); + } + cmd.arg("--target").arg("wasm32-unknown-unknown"); + cmd.output()? + }; + + if !output.status.success() { + let s = String::from_utf8_lossy(&output.stderr); + Error::cli("Running wasm tests failed", s) + } else { + for line in String::from_utf8_lossy(&output.stdout).lines() { + info!(log, "test output: {}", line); + println!("{}", line); + } + Ok(()) + } +} diff --git a/src/webdriver.rs b/src/webdriver.rs new file mode 100644 index 000000000..55e05b497 --- /dev/null +++ b/src/webdriver.rs @@ -0,0 +1,107 @@ +//! Getting WebDriver client binaries. + +use binaries::{bin_path, install_binaries_from_targz_at_url, install_binaries_from_zip_at_url}; +use command::build::BuildMode; +use error::Error; +use slog::Logger; +use std::path::{Path, PathBuf}; +use target; + +/// Get the path to an existing `chromedriver`, or install it if no existing +/// binary is found. +pub fn get_or_install_chromedriver( + log: &Logger, + crate_path: &Path, + mode: BuildMode, +) -> Result { + match (mode, bin_path(log, crate_path, "chromedriver")) { + (_, Some(path)) => Ok(path), + (BuildMode::Normal, None) => install_chromedriver(crate_path), + (BuildMode::Noinstall, None) => { + Error::crate_config("could not find `chromedriver` on the `$PATH`") + .map(|_| unreachable!()) + } + } +} + +/// Download and install a pre-built `chromedriver` binary. +pub fn install_chromedriver(crate_path: &Path) -> Result { + let target = if target::LINUX && target::x86_64 { + "linux64" + } else if target::MACOS && target::x86_64 { + "mac64" + } else if target::WINDOWS && target::x86 { + "win32" + } else { + return Err(Error::unsupported( + "geckodriver binaries are unavailable for this target", + )); + }; + + let url = format!( + "https://chromedriver.storage.googleapis.com/2.41/chromedriver_{}.zip", + target + ); + install_binaries_from_zip_at_url(crate_path, &url, Some("chromedriver"))?; + + let chromedriver = crate_path.join("bin").join("chromedriver"); + assert!(chromedriver.is_file()); + Ok(chromedriver) +} + +/// Get the path to an existing `geckodriver`, or install it if no existing +/// binary is found. +pub fn get_or_install_geckodriver( + log: &Logger, + crate_path: &Path, + mode: BuildMode, +) -> Result { + match (mode, bin_path(log, crate_path, "geckodriver")) { + (_, Some(path)) => Ok(path), + (BuildMode::Normal, None) => install_geckodriver(crate_path), + (BuildMode::Noinstall, None) => { + Error::crate_config("could not find `geckodriver` on the `$PATH`") + .map(|_| unreachable!()) + } + } +} + +/// Download and install a pre-built `geckodriver` binary. +pub fn install_geckodriver(crate_path: &Path) -> Result { + let target = if target::LINUX && target::x86 { + "linux32" + } else if target::LINUX && target::x86_64 { + "linux64" + } else if target::MACOS { + "macos" + } else if target::WINDOWS && target::x86 { + "win32" + } else if target::WINDOWS && target::x86_64 { + "win64" + } else { + return Err(Error::unsupported( + "geckodriver binaries are unavailable for this target", + )); + }; + + let url = format!( + "https://github.com/mozilla/geckodriver/releases/download/v0.21.0/geckodriver-v0.21.0-{}.tar.gz", + target + ); + install_binaries_from_targz_at_url(crate_path, &url, Some("geckodriver"))?; + + let geckodriver = crate_path.join("bin").join("geckodriver"); + assert!(geckodriver.is_file()); + Ok(geckodriver) +} + +/// Get the path to an existing `safaridriver`. We can't install `safaridriver` +/// if an existing one is not found because Apple does not provide pre-built +/// binaries. +pub fn get_safaridriver(log: &Logger, crate_path: &Path) -> Result { + if let Some(p) = bin_path(log, crate_path, "safaridriver") { + Ok(p) + } else { + Error::crate_config("could not find `safaridriver` on the `$PATH`").map(|_| unreachable!()) + } +} diff --git a/tests/all/main.rs b/tests/all/main.rs index 569989446..2fc73ecc8 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -1,16 +1,18 @@ extern crate copy_dir; extern crate failure; #[macro_use] +extern crate lazy_static; +#[macro_use] extern crate serde_derive; extern crate serde_json; extern crate structopt; extern crate tempfile; extern crate wasm_pack; -#[macro_use] -extern crate lazy_static; mod bindgen; mod build; mod manifest; mod readme; +mod test; mod utils; +mod webdriver; diff --git a/tests/all/test.rs b/tests/all/test.rs new file mode 100644 index 000000000..40017ba97 --- /dev/null +++ b/tests/all/test.rs @@ -0,0 +1,111 @@ +use utils::fixture::fixture; +use wasm_pack::command::{self, build, test, Command}; +use wasm_pack::logger; + +#[test] +fn it_can_run_node_tests() { + let fixture = fixture("tests/fixtures/wbg-test-node"); + fixture.install_local_wasm_bindgen(); + let cmd = Command::Test(test::TestOptions { + path: Some(fixture.path.clone()), + node: true, + mode: build::BuildMode::Noinstall, + ..Default::default() + }); + let logger = logger::new(&cmd, 3).unwrap(); + command::run_wasm_pack(cmd, &logger).expect("should run test command OK"); +} + +#[test] +fn it_can_run_browser_tests() { + let fixture = fixture("tests/fixtures/wbg-test-browser"); + fixture.install_local_wasm_bindgen(); + fixture.install_local_geckodriver(); + fixture.install_local_chromedriver(); + let cmd = Command::Test(test::TestOptions { + path: Some(fixture.path.clone()), + firefox: true, + chrome: true, + safari: cfg!(target_os = "macos"), + headless: true, + mode: build::BuildMode::Noinstall, + ..Default::default() + }); + let logger = logger::new(&cmd, 3).unwrap(); + command::run_wasm_pack(cmd, &logger).expect("should run test command OK"); +} + +#[test] +fn it_can_run_failing_tests() { + let fixture = fixture("tests/fixtures/wbg-test-fail"); + fixture.install_local_wasm_bindgen(); + let cmd = Command::Test(test::TestOptions { + path: Some(fixture.path.clone()), + node: true, + mode: build::BuildMode::Noinstall, + ..Default::default() + }); + let logger = logger::new(&cmd, 3).unwrap(); + assert!( + command::run_wasm_pack(cmd, &logger).is_err(), + "failing tests should return Err" + ); +} + +// #[test] +// fn it_can_find_a_webdriver_on_path() { +// let fixture = fixture("tests/fixtures/wbg-test-browser"); +// fixture.install_local_wasm_bindgen(); + +// let geckodriver = geckodriver(); +// let mut paths: Vec<_> = env::split_paths(&env::var("PATH").unwrap()).collect(); +// paths.insert(0, geckodriver); +// env::set_var("PATH", env::join_paths(paths).unwrap()); + +// let cmd = Command::Test(test::TestOptions { +// path: Some(fixture.path.clone()), +// firefox: true, +// headless: true, +// mode: build::BuildMode::Noinstall, +// ..Default::default() +// }); +// let logger = logger::new(&cmd, 3).unwrap(); +// command::run_wasm_pack(cmd, &logger).expect("should run test command OK"); +// } + +#[test] +fn it_requires_node_or_a_browser() { + let fixture = fixture("tests/fixtures/wbg-test-node"); + fixture.install_local_wasm_bindgen(); + + let cmd = Command::Test(test::TestOptions { + path: Some(fixture.path.clone()), + mode: build::BuildMode::Noinstall, + // Note: not setting node or any browser to true here. + ..Default::default() + }); + let logger = logger::new(&cmd, 3).unwrap(); + assert!( + command::run_wasm_pack(cmd, &logger).is_err(), + "need to enable node or browser testing" + ); +} + +#[test] +fn the_headless_flag_requires_a_browser() { + let fixture = fixture("tests/fixtures/wbg-test-node"); + fixture.install_local_wasm_bindgen(); + + let cmd = Command::Test(test::TestOptions { + path: Some(fixture.path.clone()), + node: true, + mode: build::BuildMode::Noinstall, + headless: true, + ..Default::default() + }); + let logger = logger::new(&cmd, 3).unwrap(); + assert!( + command::run_wasm_pack(cmd, &logger).is_err(), + "running headless tests in node doesn't make sense" + ); +} diff --git a/tests/all/utils/fixture.rs b/tests/all/utils/fixture.rs index 3122936d7..4220b32aa 100644 --- a/tests/all/utils/fixture.rs +++ b/tests/all/utils/fixture.rs @@ -1,10 +1,16 @@ +use std::env; +use std::fs; +use std::mem::ManuallyDrop; use std::path::{Path, PathBuf}; +use std::sync::{Once, ONCE_INIT}; +use std::thread; +use wasm_pack; use copy_dir::copy_dir; use tempfile; pub struct Fixture { - pub dir: tempfile::TempDir, + pub dir: ManuallyDrop, pub path: PathBuf, } @@ -17,11 +23,21 @@ pub fn fixture

(fixture: P) -> Fixture where P: AsRef, { + // Make sure that all fixtures end up sharing a target dir, and we don't + // recompile wasm-bindgen and friends many times over. + static SET_TARGET_DIR: Once = ONCE_INIT; + SET_TARGET_DIR.call_once(|| { + env::set_var( + "CARGO_TARGET_DIR", + Path::new(env!("CARGO_MANIFEST_DIR")).join("target"), + ); + }); + let fixture = fixture .as_ref() .canonicalize() .expect("should canonicalize fixture path OK"); - let dir = tempfile::tempdir().expect("should create temporary directory OK"); + let dir = ManuallyDrop::new(tempfile::tempdir().expect("should create temporary directory OK")); let path = dir.path().join("wasm-pack"); println!( "wasm-pack: copying test fixture '{}' to temporary directory '{}'", @@ -44,3 +60,97 @@ where Fixture { dir, path } } + +impl Fixture { + /// Install a local wasm-bindgen for this fixture. + /// + /// Takes care not to re-install for every fixture, but only the one time + /// for the whole test suite. + pub fn install_local_wasm_bindgen(&self) { + static INSTALL_WASM_BINDGEN: Once = ONCE_INIT; + + let tests = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests"); + let bin = tests.join("bin"); + + INSTALL_WASM_BINDGEN.call_once(|| { + if bin.join("wasm-bindgen").is_file() { + return; + } + + const WASM_BINDGEN_VERSION: &str = "0.2.17"; + wasm_pack::bindgen::download_prebuilt_wasm_bindgen(&tests, WASM_BINDGEN_VERSION) + .or_else(|_| { + wasm_pack::bindgen::cargo_install_wasm_bindgen(&tests, WASM_BINDGEN_VERSION) + }).unwrap(); + }); + + copy_dir(bin, self.path.join("bin")).expect("could not copy `bin` directory into temp dir"); + } + + /// Download `geckodriver` and return its path. + /// + /// Takes car to ensure that only one `geckodriver` is downloaded for the whole + /// test suite. + pub fn install_local_geckodriver(&self) { + static FETCH_GECKODRIVER: Once = ONCE_INIT; + + let tests = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests"); + let bin = tests.join("bin"); + fs::create_dir_all(&bin).expect("could not create `tests/bin` directory"); + + FETCH_GECKODRIVER.call_once(|| { + if bin.join("geckodriver").is_file() { + return; + } + + wasm_pack::webdriver::install_geckodriver(&tests).unwrap(); + assert!(bin.join("geckodriver").is_file()); + }); + + let self_bin = self.path.join("bin"); + fs::create_dir_all(&self_bin).expect("could not create fixture's `bin` directory"); + + fs::copy( + bin.join("geckodriver"), + self_bin.join("geckodriver"), + ).expect("could not copy `geckodriver` to fixture directory"); + } + + + /// Download `chromedriver` and return its path. + /// + /// Takes car to ensure that only one `chromedriver` is downloaded for the whole + /// test suite. + pub fn install_local_chromedriver(&self) { + static FETCH_CHROMEDRIVER: Once = ONCE_INIT; + + let tests = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests"); + let bin = tests.join("bin"); + fs::create_dir_all(&bin).expect("could not create `tests/bin` directory"); + + FETCH_CHROMEDRIVER.call_once(|| { + if bin.join("chromedriver").is_file() { + return; + } + + wasm_pack::webdriver::install_chromedriver(&tests).unwrap(); + assert!(bin.join("chromedriver").is_file()); + }); + + let self_bin = self.path.join("bin"); + fs::create_dir_all(&self_bin).expect("could not create fixture's `bin` directory"); + + fs::copy( + bin.join("chromedriver"), + self_bin.join("chromedriver"), + ).expect("could not copy `chromedriver` to fixture directory"); + } +} + +impl Drop for Fixture { + fn drop(&mut self) { + if !thread::panicking() { + unsafe { ManuallyDrop::drop(&mut self.dir) } + } + } +} diff --git a/tests/all/webdriver.rs b/tests/all/webdriver.rs new file mode 100644 index 000000000..670e77375 --- /dev/null +++ b/tests/all/webdriver.rs @@ -0,0 +1,22 @@ +use utils::fixture::fixture; +use wasm_pack::webdriver; + +#[test] +#[cfg(any(all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "macos", target_arch = "x86_64"), + all(target_os = "windows", target_arch = "x86")))] +fn can_install_chromedriver() { + let fixture = fixture("tests/fixtures/js-hello-world"); + assert!(webdriver::install_chromedriver(&fixture.path).is_ok()); +} + +#[test] +#[cfg(any(all(target_os = "linux", target_arch = "x86"), + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "macos", target_arch = "x86_64"), + all(target_os = "windows", target_arch = "x86"), + all(target_os = "windows", target_arch = "x86_64")))] +fn can_install_geckodriver() { + let fixture = fixture("tests/fixtures/js-hello-world"); + assert!(webdriver::install_geckodriver(&fixture.path).is_ok()); +} diff --git a/tests/fixtures/wbg-test-browser/Cargo.toml b/tests/fixtures/wbg-test-browser/Cargo.toml new file mode 100644 index 000000000..16254fcea --- /dev/null +++ b/tests/fixtures/wbg-test-browser/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "wbg-test-browser" +version = "0.1.0" +authors = ["Nick Fitzgerald "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wasm-bindgen = "0.2.17" + +[dev-dependencies] +wasm-bindgen-test = "0.2.17" diff --git a/tests/fixtures/wbg-test-browser/src/lib.rs b/tests/fixtures/wbg-test-browser/src/lib.rs new file mode 100644 index 000000000..31e1bb209 --- /dev/null +++ b/tests/fixtures/wbg-test-browser/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/tests/fixtures/wbg-test-browser/tests/browser.rs b/tests/fixtures/wbg-test-browser/tests/browser.rs new file mode 100644 index 000000000..dbdf09964 --- /dev/null +++ b/tests/fixtures/wbg-test-browser/tests/browser.rs @@ -0,0 +1,9 @@ +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +fn pass() { + assert_eq!(1, 1); +} diff --git a/tests/fixtures/wbg-test-fail/Cargo.toml b/tests/fixtures/wbg-test-fail/Cargo.toml new file mode 100644 index 000000000..791969caf --- /dev/null +++ b/tests/fixtures/wbg-test-fail/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "wbg-test-ok" +version = "0.1.0" +authors = ["Nick Fitzgerald "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wasm-bindgen = "0.2.17" + +[dev-dependencies] +wasm-bindgen-test = "0.2.17" diff --git a/tests/fixtures/wbg-test-fail/src/lib.rs b/tests/fixtures/wbg-test-fail/src/lib.rs new file mode 100644 index 000000000..31e1bb209 --- /dev/null +++ b/tests/fixtures/wbg-test-fail/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/tests/fixtures/wbg-test-fail/tests/node.rs b/tests/fixtures/wbg-test-fail/tests/node.rs new file mode 100644 index 000000000..bb2c0497d --- /dev/null +++ b/tests/fixtures/wbg-test-fail/tests/node.rs @@ -0,0 +1,7 @@ +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; + +#[wasm_bindgen_test] +fn fail() { + assert_eq!(1, 2); +} diff --git a/tests/fixtures/wbg-test-node/Cargo.toml b/tests/fixtures/wbg-test-node/Cargo.toml new file mode 100644 index 000000000..e19ff4c39 --- /dev/null +++ b/tests/fixtures/wbg-test-node/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "wbg-test-node" +version = "0.1.0" +authors = ["Nick Fitzgerald "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wasm-bindgen = "0.2.17" + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen-test = "0.2.17" diff --git a/tests/fixtures/wbg-test-node/src/lib.rs b/tests/fixtures/wbg-test-node/src/lib.rs new file mode 100644 index 000000000..31e1bb209 --- /dev/null +++ b/tests/fixtures/wbg-test-node/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/tests/fixtures/wbg-test-node/tests/node.rs b/tests/fixtures/wbg-test-node/tests/node.rs new file mode 100644 index 000000000..84e09f5de --- /dev/null +++ b/tests/fixtures/wbg-test-node/tests/node.rs @@ -0,0 +1,8 @@ +extern crate wasm_bindgen_test; + +use wasm_bindgen_test::*; + +#[wasm_bindgen_test] +fn pass() { + assert_eq!(1, 1); +}