Skip to content

Commit 041438e

Browse files
committed
feature: Implement a wasm-pack test subcommand
This is a wrapper over `cargo test --target wasm32-unknown-unknown` and the `wasm-bindgen-test` crate. It will run tests in both node and browsers using WebDriver clients. Fixes #248
1 parent 89be40d commit 041438e

34 files changed

+1435
-159
lines changed

.appveyor.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
install:
2+
- ps: Install-Product node 10
23
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
34
- if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly
45
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
6+
- set RUST_BACKTRACE=1
7+
- rustup target add wasm32-unknown-unknown --toolchain nightly
58
- rustc -V
69
- cargo -V
710

811
build: false
912

1013
test_script:
11-
- cargo test --locked -- --test-threads 1
14+
- cargo test --locked
1215

1316
before_deploy:
1417
- ps: |

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ target/
22
**/target
33
**/*.rs.bk
44
**/pkg
5-
tests/fixtures/**/Cargo.lock
5+
tests/.crates.toml
6+
tests/bin
67
wasm-pack.log

.travis.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
language: rust
22
sudo: false
33

4+
INSTALL_NODE_VIA_NVM: &INSTALL_NODE_VIA_NVM
5+
|
6+
rustup target add wasm32-unknown-unknown
7+
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
8+
source ~/.nvm/nvm.sh
9+
nvm install v10.5
10+
411
DEPLOY_TO_GITHUB: &DEPLOY_TO_GITHUB
512
before_deploy:
613
- git config --local user.name "Ashley Williams"
@@ -26,9 +33,13 @@ matrix:
2633
include:
2734

2835
# tests pass
29-
3036
- env: JOB=test RUST_BACKTRACE=1
3137
rust: nightly
38+
addons:
39+
firefox: latest
40+
chrome: stable
41+
install:
42+
- *INSTALL_NODE_VIA_NVM
3243
script:
3344
- cargo test --locked
3445
- rustup component add rustfmt-preview

Cargo.lock

Lines changed: 112 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ structopt = "0.2"
2828
tar = "0.4.16"
2929
toml = "0.4"
3030
which = "2.0.0"
31+
zip = "0.4.2"
3132

3233
[dev-dependencies]
3334
copy_dir = "0.1.2"

src/binaries.rs

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
//! Utilities for finding and installing binaries that we depend on.
2+
3+
use curl;
4+
use error::Error;
5+
use failure;
6+
use flate2;
7+
use slog::Logger;
8+
use std::collections::HashSet;
9+
use std::ffi;
10+
use std::fs;
11+
use std::io;
12+
use std::path::{Path, PathBuf};
13+
use tar;
14+
use which::which;
15+
use zip;
16+
17+
/// Get the local (at `$CRATE/bin/$BIN`; preferred) or global (on `$PATH`) path
18+
/// for the given binary.
19+
pub fn bin_path(log: &Logger, crate_path: &Path, bin: &str) -> Option<PathBuf> {
20+
assert!(!bin.ends_with(".exe"));
21+
debug!(log, "Searching for {} binary...", bin);
22+
23+
// Return the path to the local binary, if it exists.
24+
let local_path = |crate_path: &Path| -> Option<PathBuf> {
25+
let mut p = crate_path.to_path_buf();
26+
p.push("bin");
27+
if cfg!(target_os = "windows") {
28+
let mut bin = bin.to_string();
29+
bin.push_str(".exe");
30+
p.push(bin);
31+
} else {
32+
p.push(bin);
33+
}
34+
35+
debug!(log, "Checking for local {} binary at {}", bin, p.display());
36+
if p.is_file() {
37+
Some(p)
38+
} else {
39+
None
40+
}
41+
};
42+
43+
// Return the path to the global binary, if it exists.
44+
let global_path = || -> Option<PathBuf> {
45+
debug!(log, "Looking for global {} binary on $PATH", bin);
46+
if let Ok(p) = which(bin) {
47+
Some(p)
48+
} else {
49+
None
50+
}
51+
};
52+
53+
local_path(crate_path)
54+
.or_else(global_path)
55+
.map(|p| {
56+
let p = p.canonicalize().unwrap_or(p);
57+
debug!(log, "Using {} binary at {}", bin, p.display());
58+
p
59+
}).or_else(|| {
60+
debug!(log, "Could not find {} binary.", bin);
61+
None
62+
})
63+
}
64+
65+
fn curl(url: &str) -> Result<Vec<u8>, failure::Error> {
66+
let mut data = Vec::new();
67+
68+
fn with_url_context<T, E>(url: &str, r: Result<T, E>) -> Result<T, impl failure::Fail>
69+
where
70+
Result<T, E>: failure::ResultExt<T, E>,
71+
{
72+
use failure::ResultExt;
73+
r.with_context(|_| format!("when requesting {}", url))
74+
}
75+
76+
let mut easy = curl::easy::Easy::new();
77+
with_url_context(url, easy.follow_location(true))?;
78+
with_url_context(url, easy.url(url))?;
79+
80+
{
81+
let mut transfer = easy.transfer();
82+
with_url_context(
83+
url,
84+
transfer.write_function(|part| {
85+
data.extend_from_slice(part);
86+
Ok(part.len())
87+
}),
88+
)?;
89+
with_url_context(url, transfer.perform())?;
90+
}
91+
92+
let code = with_url_context(url, easy.response_code())?;
93+
if 200 <= code && code < 300 {
94+
Ok(data)
95+
} else {
96+
Err(Error::http(&format!(
97+
"received a bad HTTP status code ({}) when requesting {}",
98+
code, url
99+
)).into())
100+
}
101+
}
102+
103+
/// Download the `.tar.gz` file at the given URL and unpack the given binaries
104+
/// from it into the given crate.
105+
///
106+
/// Upon success, every `$BIN` in `binaries` will be at `$CRATE/bin/$BIN`.
107+
pub fn install_binaries_from_targz_at_url<'a, I>(
108+
crate_path: &Path,
109+
url: &str,
110+
binaries: I,
111+
) -> Result<(), Error>
112+
where
113+
I: IntoIterator<Item = &'a str>,
114+
{
115+
let mut binaries: HashSet<_> = binaries.into_iter().map(ffi::OsStr::new).collect();
116+
117+
let tarball = curl(&url).map_err(|e| Error::http(&e.to_string()))?;
118+
let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(&tarball[..]));
119+
120+
let bin = crate_path.join("bin");
121+
fs::create_dir_all(&bin)?;
122+
123+
for entry in archive.entries()? {
124+
let mut entry = entry?;
125+
126+
let dest = match entry.path()?.file_stem() {
127+
Some(f) if binaries.contains(f) => {
128+
binaries.remove(f);
129+
bin.join(entry.path()?.file_name().unwrap())
130+
}
131+
_ => continue,
132+
};
133+
134+
entry.unpack(dest)?;
135+
}
136+
137+
if binaries.is_empty() {
138+
Ok(())
139+
} else {
140+
Err(Error::archive(&format!(
141+
"the tarball at {} was missing expected executables: {}",
142+
url,
143+
binaries
144+
.into_iter()
145+
.map(|s| s.to_string_lossy())
146+
.collect::<Vec<_>>()
147+
.join(", "),
148+
)))
149+
}
150+
}
151+
152+
/// Install binaries from within the given zip at the given URL.
153+
///
154+
/// Upon success, the binaries will be at the `$CRATE/bin/$BIN` path.
155+
pub fn install_binaries_from_zip_at_url<'a, I>(
156+
crate_path: &Path,
157+
url: &str,
158+
binaries: I,
159+
) -> Result<(), Error>
160+
where
161+
I: IntoIterator<Item = &'a str>,
162+
{
163+
let mut binaries: HashSet<_> = binaries.into_iter().map(ffi::OsStr::new).collect();
164+
165+
let data = curl(&url).map_err(|e| Error::http(&e.to_string()))?;
166+
let data = io::Cursor::new(data);
167+
let mut zip = zip::ZipArchive::new(data)?;
168+
169+
let bin = crate_path.join("bin");
170+
fs::create_dir_all(&bin)?;
171+
172+
for i in 0..zip.len() {
173+
let mut entry = zip.by_index(i).unwrap();
174+
let entry_path = entry.sanitized_name();
175+
match entry_path.file_stem() {
176+
Some(f) if binaries.contains(f) => {
177+
binaries.remove(f);
178+
let mut dest = bin_open_options()
179+
.write(true)
180+
.create_new(true)
181+
.open(bin.join(entry_path.file_name().unwrap()))?;
182+
io::copy(&mut entry, &mut dest)?;
183+
}
184+
_ => continue,
185+
};
186+
}
187+
188+
if binaries.is_empty() {
189+
Ok(())
190+
} else {
191+
Err(Error::archive(&format!(
192+
"the zip at {} was missing expected executables: {}",
193+
url,
194+
binaries
195+
.into_iter()
196+
.map(|s| s.to_string_lossy())
197+
.collect::<Vec<_>>()
198+
.join(", "),
199+
)))
200+
}
201+
}
202+
203+
#[cfg(unix)]
204+
fn bin_open_options() -> fs::OpenOptions {
205+
use std::os::unix::fs::OpenOptionsExt;
206+
207+
let mut opts = fs::OpenOptions::new();
208+
opts.mode(0o755);
209+
opts
210+
}
211+
212+
#[cfg(not(unix))]
213+
fn bin_open_options() -> fs::OpenOptions {
214+
fs::OpenOptions::new()
215+
}

0 commit comments

Comments
 (0)