This uses `wasm-bindgen-test` under the hood. Fixes #248master
parent
2cd4504b64
commit
577d1b33e6
@ -1,6 +1,5 @@ |
||||
target/ |
||||
**/target |
||||
**/*.rs.bk |
||||
**/pkg |
||||
tests/fixtures/**/Cargo.lock |
||||
tests/.crates.toml |
||||
tests/bin |
||||
wasm-pack.log |
||||
|
@ -0,0 +1,243 @@ |
||||
//! 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 target; |
||||
use which::which; |
||||
use zip; |
||||
|
||||
/// Get the path for a crate's directory of locally-installed binaries.
|
||||
///
|
||||
/// This does not check whether or ensure that the directory exists.
|
||||
pub fn local_bin_dir(crate_path: &Path) -> PathBuf { |
||||
crate_path.join("bin") |
||||
} |
||||
|
||||
/// Ensure that the crate's directory for locally-installed binaries exists.
|
||||
pub fn ensure_local_bin_dir(crate_path: &Path) -> io::Result<()> { |
||||
fs::create_dir_all(local_bin_dir(crate_path)) |
||||
} |
||||
|
||||
/// Get the path for where `bin` would be if we have a crate-local install for
|
||||
/// it.
|
||||
///
|
||||
/// This does *not* check whether there is a file at that path or not.
|
||||
///
|
||||
/// This will automatically add the `.exe` extension for windows.
|
||||
pub fn local_bin_path(crate_path: &Path, bin: &str) -> PathBuf { |
||||
let mut p = local_bin_dir(crate_path).join(bin); |
||||
if target::WINDOWS { |
||||
p.set_extension("exe"); |
||||
} |
||||
p |
||||
} |
||||
|
||||
/// Get the local (at `$CRATE/bin/$BIN`; preferred) or global (on `$PATH`) path
|
||||
/// for the given binary.
|
||||
///
|
||||
/// If this function returns `Some(path)`, then a file at that path exists (or
|
||||
/// at least existed when we checked! In general, we aren't really worried about
|
||||
/// racing with an uninstall of a tool that we rely on.)
|
||||
pub fn bin_path(log: &Logger, crate_path: &Path, bin: &str) -> Option<PathBuf> { |
||||
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<PathBuf> { |
||||
let p = local_bin_path(crate_path, 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<PathBuf> { |
||||
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 with_url_context<T, E>(url: &str, r: Result<T, E>) -> Result<T, impl failure::Fail> |
||||
where |
||||
Result<T, E>: failure::ResultExt<T, E>, |
||||
{ |
||||
use failure::ResultExt; |
||||
r.with_context(|_| format!("when requesting {}", url)) |
||||
} |
||||
|
||||
fn transfer( |
||||
url: &str, |
||||
easy: &mut curl::easy::Easy, |
||||
data: &mut Vec<u8>, |
||||
) -> Result<(), failure::Error> { |
||||
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())?; |
||||
Ok(()) |
||||
} |
||||
|
||||
fn curl(url: &str) -> Result<Vec<u8>, failure::Error> { |
||||
let mut data = Vec::new(); |
||||
|
||||
let mut easy = curl::easy::Easy::new(); |
||||
with_url_context(url, easy.follow_location(true))?; |
||||
with_url_context(url, easy.url(url))?; |
||||
transfer(url, &mut easy, &mut data)?; |
||||
|
||||
let status_code = with_url_context(url, easy.response_code())?; |
||||
if 200 <= status_code && status_code < 300 { |
||||
Ok(data) |
||||
} else { |
||||
Err(Error::http(&format!( |
||||
"received a bad HTTP status code ({}) when requesting {}", |
||||
status_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<Item = &'a str>, |
||||
{ |
||||
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[..])); |
||||
|
||||
ensure_local_bin_dir(crate_path)?; |
||||
let bin = local_bin_dir(crate_path); |
||||
|
||||
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::<Vec<_>>() |
||||
.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<Item = &'a str>, |
||||
{ |
||||
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)?; |
||||
|
||||
ensure_local_bin_dir(crate_path)?; |
||||
let bin = local_bin_dir(crate_path); |
||||
|
||||
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::<Vec<_>>() |
||||
.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() |
||||
} |
@ -0,0 +1,402 @@ |
||||
//! 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::{self, 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<PathBuf>, |
||||
|
||||
#[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<PathBuf>, |
||||
|
||||
#[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<PathBuf>, |
||||
|
||||
#[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<PathBuf>, |
||||
|
||||
#[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<PathBuf>, |
||||
chrome: bool, |
||||
chromedriver: Option<PathBuf>, |
||||
safari: bool, |
||||
safaridriver: Option<PathBuf>, |
||||
headless: bool, |
||||
release: bool, |
||||
test_runner_path: Option<PathBuf>, |
||||
} |
||||
|
||||
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<Self, Error> { |
||||
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. Node does not provide a UI, \ |
||||
so it doesn't make sense to talk about a headless version of Node 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) |
||||
} |
||||
} |
@ -0,0 +1,15 @@ |
||||
//! Information about the target wasm-pack is currently being compiled for.
|
||||
//!
|
||||
//! That is, whether we are building wasm-pack for windows vs linux, and x86 vs
|
||||
//! x86-64, etc.
|
||||
|
||||
#![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"); |
@ -0,0 +1,51 @@ |
||||
//! Testing a Rust crate compiled to wasm.
|
||||
|
||||
pub mod webdriver; |
||||
|
||||
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<I, K, V>( |
||||
path: &Path, |
||||
release: bool, |
||||
log: &Logger, |
||||
envs: I, |
||||
) -> Result<(), Error> |
||||
where |
||||
I: IntoIterator<Item = (K, V)>, |
||||
K: AsRef<OsStr>, |
||||
V: AsRef<OsStr>, |
||||
{ |
||||
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(()) |
||||
} |
||||
} |
@ -0,0 +1,134 @@ |
||||
//! Getting WebDriver client binaries.
|
||||
|
||||
use binaries::{ |
||||
self, 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<PathBuf, Error> { |
||||
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( |
||||
"No crate-local `chromedriver` binary found, and could not find a global \ |
||||
`chromedriver` on the `$PATH`. Not installing `chromedriver` because of noinstall \ |
||||
mode.", |
||||
).map(|_| unreachable!()), |
||||
} |
||||
} |
||||
|
||||
fn get_local_chromedriver_path(crate_path: &Path) -> PathBuf { |
||||
binaries::local_bin_path(crate_path, "chromedriver") |
||||
} |
||||
|
||||
fn get_chromedriver_url() -> Result<String, Error> { |
||||
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", |
||||
)); |
||||
}; |
||||
|
||||
Ok(format!( |
||||
"https://chromedriver.storage.googleapis.com/2.41/chromedriver_{}.zip", |
||||
target |
||||
)) |
||||
} |
||||
|
||||
/// Download and install a pre-built `chromedriver` binary.
|
||||
pub fn install_chromedriver(crate_path: &Path) -> Result<PathBuf, Error> { |
||||
let url = get_chromedriver_url()?; |
||||
install_binaries_from_zip_at_url(crate_path, &url, Some("chromedriver"))?; |
||||
let chromedriver = get_local_chromedriver_path(crate_path); |
||||
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<PathBuf, Error> { |
||||
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( |
||||
"No crate-local `geckodriver` binary found, and could not find a global `geckodriver` \ |
||||
on the `$PATH`. Not installing `geckodriver` because of noinstall mode.", |
||||
).map(|_| unreachable!()), |
||||
} |
||||
} |
||||
|
||||
fn get_geckodriver_url() -> Result<String, Error> { |
||||
let (target, ext) = if target::LINUX && target::x86 { |
||||
("linux32", "tar.gz") |
||||
} else if target::LINUX && target::x86_64 { |
||||
("linux64", "tar.gz") |
||||
} else if target::MACOS { |
||||
("macos", "tar.gz") |
||||
} else if target::WINDOWS && target::x86 { |
||||
("win32", "zip") |
||||
} else if target::WINDOWS && target::x86_64 { |
||||
("win64", "zip") |
||||
} else { |
||||
return Err(Error::unsupported( |
||||
"geckodriver binaries are unavailable for this target", |
||||
)); |
||||
}; |
||||
|
||||
Ok(format!( |
||||
"https://github.com/mozilla/geckodriver/releases/download/v0.21.0/geckodriver-v0.21.0-{}.{}", |
||||
target, |
||||
ext, |
||||
)) |
||||
} |
||||
|
||||
fn get_local_geckodriver_path(crate_path: &Path) -> PathBuf { |
||||
binaries::local_bin_path(crate_path, "geckodriver") |
||||
} |
||||
|
||||
/// Download and install a pre-built `geckodriver` binary.
|
||||
pub fn install_geckodriver(crate_path: &Path) -> Result<PathBuf, Error> { |
||||
let url = get_geckodriver_url()?; |
||||
|
||||
if url.ends_with("tar.gz") { |
||||
install_binaries_from_targz_at_url(crate_path, &url, Some("geckodriver"))?; |
||||
} else { |
||||
assert!(url.ends_with("zip")); |
||||
install_binaries_from_zip_at_url(crate_path, &url, Some("geckodriver"))?; |
||||
} |
||||
|
||||
let geckodriver = get_local_geckodriver_path(crate_path); |
||||
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. However, `safaridriver` *should*
|
||||
/// be present by default.
|
||||
pub fn get_safaridriver(log: &Logger, crate_path: &Path) -> Result<PathBuf, Error> { |
||||
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!()) |
||||
} |
||||
} |
@ -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; |
||||
|
@ -0,0 +1,164 @@ |
||||
use std::env; |
||||
use std::fs; |
||||
use tempfile; |
||||
use utils::fixture::fixture; |
||||
use wasm_pack::binaries; |
||||
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] |
||||
#[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"), |
||||
all(target_os = "windows", target_arch = "x86_64") |
||||
))] |
||||
fn it_can_run_browser_tests() { |
||||
let fixture = fixture("tests/fixtures/wbg-test-browser"); |
||||
fixture.install_local_wasm_bindgen(); |
||||
|
||||
let firefox = 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") |
||||
)); |
||||
if firefox { |
||||
fixture.install_local_geckodriver(); |
||||
} |
||||
|
||||
let chrome = 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") |
||||
)); |
||||
if chrome { |
||||
fixture.install_local_chromedriver(); |
||||
} |
||||
|
||||
let safari = cfg!(target_os = "macos"); |
||||
|
||||
if !firefox && !chrome && !safari { |
||||
return; |
||||
} |
||||
|
||||
let cmd = Command::Test(test::TestOptions { |
||||
path: Some(fixture.path.clone()), |
||||
firefox, |
||||
chrome, |
||||
safari, |
||||
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] |
||||
#[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 it_can_find_a_webdriver_on_path() { |
||||
let fixture = fixture("tests/fixtures/wbg-test-browser"); |
||||
fixture.install_local_wasm_bindgen(); |
||||
fixture.install_local_geckodriver(); |
||||
|
||||
let geckodriver_dir = tempfile::TempDir::new().unwrap(); |
||||
let local_geckodriver = binaries::local_bin_path(&fixture.path, "geckodriver"); |
||||
fs::copy( |
||||
&local_geckodriver, |
||||
geckodriver_dir |
||||
.path() |
||||
.join(local_geckodriver.file_name().unwrap()), |
||||
).unwrap(); |
||||
fs::remove_file(&local_geckodriver).unwrap(); |
||||
|
||||
let mut paths: Vec<_> = env::split_paths(&env::var("PATH").unwrap()).collect(); |
||||
paths.insert(0, geckodriver_dir.path().into()); |
||||
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" |
||||
); |
||||
} |
@ -0,0 +1,26 @@ |
||||
use utils::fixture::fixture; |
||||
use wasm_pack::test::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()); |
||||
} |
@ -0,0 +1,16 @@ |
||||
[package] |
||||
name = "wbg-test-node" |
||||
version = "0.1.0" |
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"] |
||||
|
||||
[lib] |
||||
crate-type = ["cdylib"] |
||||
|
||||
[dependencies] |
||||
# We depend on wasm-bindgen 0.2.21 |
||||
wasm-bindgen = "0.2.21" |
||||
|
||||
[dev-dependencies] |
||||
# And we depend on wasm-bindgen-test 0.2.19. But this should match the |
||||
# wasm-bindgen dependency! |
||||
wasm-bindgen-test = "0.2.19" |
@ -0,0 +1,7 @@ |
||||
#[cfg(test)] |
||||
mod tests { |
||||
#[test] |
||||
fn it_works() { |
||||
assert_eq!(2 + 2, 4); |
||||
} |
||||
} |
@ -0,0 +1,8 @@ |
||||
extern crate wasm_bindgen_test; |
||||
|
||||
use wasm_bindgen_test::*; |
||||
|
||||
#[wasm_bindgen_test] |
||||
fn pass() { |
||||
assert_eq!(1, 1); |
||||
} |
@ -0,0 +1,13 @@ |
||||
[package] |
||||
name = "wbg-test-browser" |
||||
version = "0.1.0" |
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"] |
||||
|
||||
[lib] |
||||
crate-type = ["cdylib"] |
||||
|
||||
[dependencies] |
||||
wasm-bindgen = "0.2.21" |
||||
|
||||
[dev-dependencies] |
||||
wasm-bindgen-test = "0.2.21" |
@ -0,0 +1,5 @@ |
||||
extern crate wasm_bindgen; |
||||
use wasm_bindgen::prelude::*; |
||||
|
||||
#[wasm_bindgen] |
||||
pub fn hello() {} |
@ -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); |
||||
} |
@ -0,0 +1,13 @@ |
||||
[package] |
||||
name = "wbg-test-ok" |
||||
version = "0.1.0" |
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"] |
||||
|
||||
[lib] |
||||
crate-type = ["cdylib"] |
||||
|
||||
[dependencies] |
||||
wasm-bindgen = "0.2.21" |
||||
|
||||
[dev-dependencies] |
||||
wasm-bindgen-test = "0.2.21" |
@ -0,0 +1,5 @@ |
||||
extern crate wasm_bindgen; |
||||
use wasm_bindgen::prelude::*; |
||||
|
||||
#[wasm_bindgen] |
||||
pub fn hi() {} |
@ -0,0 +1,7 @@ |
||||
extern crate wasm_bindgen_test; |
||||
use wasm_bindgen_test::*; |
||||
|
||||
#[wasm_bindgen_test] |
||||
fn fail() { |
||||
assert_eq!(1, 2); |
||||
} |
@ -0,0 +1,13 @@ |
||||
[package] |
||||
name = "wbg-test-node" |
||||
version = "0.1.0" |
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"] |
||||
|
||||
[lib] |
||||
crate-type = ["cdylib"] |
||||
|
||||
[dependencies] |
||||
wasm-bindgen = "0.2.21" |
||||
|
||||
[dev-dependencies] |
||||
wasm-bindgen-test = "0.2.21" |
@ -0,0 +1,7 @@ |
||||
#[cfg(test)] |
||||
mod tests { |
||||
#[test] |
||||
fn it_works() { |
||||
assert_eq!(2 + 2, 4); |
||||
} |
||||
} |
@ -0,0 +1,8 @@ |
||||
extern crate wasm_bindgen_test; |
||||
|
||||
use wasm_bindgen_test::*; |
||||
|
||||
#[wasm_bindgen_test] |
||||
fn pass() { |
||||
assert_eq!(1, 1); |
||||
} |
Loading…
Reference in new issue