use binary_install::Cache;
use lazy_static::lazy_static;
use std::env;
use std::fs;
use std::mem::ManuallyDrop;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::{MutexGuard, Once};
use std::thread;
use tempfile::TempDir;
use wasm_pack;
use wasm_pack::install::{self, Tool};
pub struct Fixture {
pub dir: ManuallyDrop<TempDir>,
pub path: PathBuf,
}
impl Fixture {
pub fn new() -> Fixture {
static SET_TARGET_DIR: Once = Once::new();
let target_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("target");
SET_TARGET_DIR.call_once(|| {
env::set_var("CARGO_TARGET_DIR", &target_dir);
});
let root = target_dir.join("t");
fs::create_dir_all(&root).unwrap();
let dir = TempDir::new_in(&root).unwrap();
let path = dir.path().join("wasm-pack");
eprintln!("Created fixture at {}", path.display());
Fixture {
dir: ManuallyDrop::new(dir),
path,
}
}
pub fn file<P: AsRef<Path>, C: AsRef<[u8]>>(&self, path: P, contents: C) -> &Self {
assert!(path.as_ref().is_relative());
let path = self.path.join(path);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).unwrap();
}
fs::write(path, contents).unwrap();
self
}
pub fn readme(&self) -> &Self {
self.file(
"README.md",
r#"
# Fixture!
> an example rust -> wasm project
"#,
)
}
pub fn license(&self) -> &Self {
self.file(
"LICENSE",
r#"
I'm a license!
"#,
)
}
pub fn wtfpl_license(&self) -> &Self {
self.file(
"LICENSE-WTFPL",
r#"
DO WHATEVER YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHATEVER YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHATEVER YOU WANT TO.
"#,
)
}
pub fn mit_license(&self) -> &Self {
self.file(
"LICENSE-MIT",
r#"
Copyright <YEAR> <COPYRIGHT HOLDER>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"#,
)
}
pub fn cargo_toml(&self, name: &str) -> &Self {
self.file(
"Cargo.toml",
&format!(
r#"
[package]
authors = ["The wasm-pack developers"]
description = "so awesome rust+wasm package"
license = "WTFPL"
name = "{}"
repository = "https://github.com/drager/wasm-pack.git"
version = "0.1.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
# Note that this uses and `=` dependency because there are
# various tests which assert that the version of wasm
# bindgen downloaded is what we expect, and if `=` is
# removed then it will download whatever the newest version
# of wasm-bindgen is which may not be what's listed here.
wasm-bindgen = "=0.2.100"
[dev-dependencies]
wasm-bindgen-test = "0.3"
"#,
name
),
)
}
pub fn cargo_toml_with_custom_profile(&self, name: &str, profile_name: &str) -> &Self {
self.file(
"Cargo.toml",
&format!(
r#"
[package]
authors = ["The wasm-pack developers"]
description = "so awesome rust+wasm package"
license = "WTFPL"
name = "{}"
repository = "https://github.com/drager/wasm-pack.git"
version = "0.1.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
# Note that this uses and `=` dependency because there are
# various tests which assert that the version of wasm
# bindgen downloaded is what we expect, and if `=` is
# removed then it will download whatever the newest version
# of wasm-bindgen is which may not be what's listed here.
wasm-bindgen = "=0.2.100"
[dev-dependencies]
wasm-bindgen-test = "0.3"
[profile.{}]
inherits = "release"
opt-level = 'z'
lto = true
"#,
name, profile_name
),
)
}
pub fn cargo_toml_with_license_file(&self, name: &str, license_file: &str) -> &Self {
self.file(
"Cargo.toml",
&format!(
r#"
[package]
authors = ["The wasm-pack developers"]
description = "so awesome rust+wasm package"
name = "{}"
license-file = "{}"
repository = "https://github.com/drager/wasm-pack.git"
version = "0.1.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "=0.2.100"
[dev-dependencies]
wasm-bindgen-test = "=0.2.21"
"#,
name, license_file
),
)
}
pub fn hello_world_src_lib(&self) -> &Self {
self.file(
"src/lib.rs",
r#"
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
// Import the `window.alert` function from the Web.
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
// Export a `greet` function from Rust to JavaScript, that alerts a
// hello message.
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
"#,
)
}
pub fn install_local_wasm_bindgen(&self) -> PathBuf {
self.install_wasm_opt();
static INSTALL_WASM_BINDGEN: Once = Once::new();
let cache = self.cache();
let version = "0.2.100";
let download = || {
if let Ok(download) =
install::download_prebuilt(&Tool::WasmBindgen, &cache, version, true)
{
return Ok(download);
}
install::cargo_install(Tool::WasmBindgen, &cache, version, true)
};
INSTALL_WASM_BINDGEN.call_once(|| {
download().unwrap();
});
if let install::Status::Found(dl) = download().unwrap() {
dl.binary("wasm-bindgen").unwrap()
} else {
panic!("Download failed")
}
}
pub fn install_wasm_opt(&self) {
static INSTALL_WASM_OPT: Once = Once::new();
let cache = self.cache();
INSTALL_WASM_OPT.call_once(|| {
wasm_pack::wasm_opt::find_wasm_opt(&cache, true).unwrap();
});
}
pub fn install_local_cargo_generate(&self) -> PathBuf {
static INSTALL_CARGO_GENERATE: Once = Once::new();
let cache = self.cache();
let download = || {
if let Ok(download) =
install::download_prebuilt(&Tool::CargoGenerate, &cache, "latest", true)
{
return Ok(download);
}
install::cargo_install(Tool::CargoGenerate, &cache, "latest", true)
};
INSTALL_CARGO_GENERATE.call_once(|| {
download().unwrap();
});
if let install::Status::Found(dl) = download().unwrap() {
dl.binary("cargo-generate").unwrap()
} else {
panic!("Download failed")
}
}
pub fn install_local_geckodriver(&self) -> PathBuf {
static FETCH_GECKODRIVER: Once = Once::new();
let cache = self.cache();
FETCH_GECKODRIVER.call_once(|| {
wasm_pack::test::webdriver::install_geckodriver(&cache, true).unwrap();
});
wasm_pack::test::webdriver::install_geckodriver(&cache, true).unwrap()
}
pub fn install_local_chromedriver(&self) -> PathBuf {
static FETCH_CHROMEDRIVER: Once = Once::new();
let cache = self.cache();
FETCH_CHROMEDRIVER.call_once(|| {
wasm_pack::test::webdriver::install_chromedriver(&cache, true).unwrap();
});
wasm_pack::test::webdriver::install_chromedriver(&cache, true).unwrap()
}
pub fn cache_dir(&self) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("target")
.join("test_cache")
}
pub fn cache(&self) -> Cache {
let cache_dir = self.cache_dir();
fs::create_dir_all(&cache_dir).unwrap();
Cache::at(&cache_dir)
}
pub fn cargo_check(&self) -> &Self {
Command::new("cargo")
.current_dir(&self.path)
.arg("check")
.arg("--target")
.arg("wasm32-unknown-unknown")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.unwrap();
self
}
pub fn wasm_pack(&self) -> Command {
use assert_cmd::cargo;
let mut cmd = Command::new(cargo::cargo_bin!(env!("CARGO_PKG_NAME")));
cmd.current_dir(&self.path);
cmd.env("WASM_PACK_CACHE", self.cache_dir());
cmd.env_remove("CARGO_TERM_COLOR");
cmd
}
pub fn lock(&self) -> MutexGuard<'static, ()> {
use std::sync::Mutex;
lazy_static! {
static ref ONE_TEST_AT_A_TIME: Mutex<()> = Mutex::new(());
}
ONE_TEST_AT_A_TIME.lock().unwrap_or_else(|e| e.into_inner())
}
}
impl Drop for Fixture {
fn drop(&mut self) {
if !thread::panicking() {
unsafe { ManuallyDrop::drop(&mut self.dir) }
}
}
}
pub fn bad_cargo_toml() -> Fixture {
let fixture = Fixture::new();
fixture.readme().hello_world_src_lib().file(
"Cargo.toml",
r#"
[package]
name = "bad-cargo-toml"
version = "0.1.0"
authors = ["The wasm-pack developers"]
[lib]
crate-type = ["foo"]
[dependencies]
# Note: no wasm-bindgen dependency!
"#,
);
fixture
}
pub fn js_hello_world() -> Fixture {
let fixture = Fixture::new();
fixture
.readme()
.cargo_toml("js-hello-world")
.hello_world_src_lib();
fixture
}
pub fn js_hello_world_with_custom_profile(profile_name: &str) -> Fixture {
let fixture = Fixture::new();
fixture
.readme()
.cargo_toml_with_custom_profile("js-hello-world", profile_name)
.hello_world_src_lib();
fixture
}
pub fn no_cdylib() -> Fixture {
let fixture = Fixture::new();
fixture.readme().hello_world_src_lib().file(
"Cargo.toml",
r#"
[package]
authors = ["The wasm-pack developers"]
description = "so awesome rust+wasm package"
license = "WTFPL"
name = "foo"
repository = "https://github.com/drager/wasm-pack.git"
version = "0.1.0"
# [lib]
# crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
[dev-dependencies]
wasm-bindgen-test = "0.3"
"#,
);
fixture
}
pub fn not_a_crate() -> Fixture {
let fixture = Fixture::new();
fixture.file("README.md", "This is not a Rust crate!");
fixture
}
pub fn serde_feature() -> Fixture {
let fixture = Fixture::new();
fixture.readme().hello_world_src_lib().file(
"Cargo.toml",
r#"
[package]
name = "serde-serialize"
version = "0.1.0"
authors = ["The wasm-pack developers"]
[lib]
crate-type = ["cdylib"]
[dependencies.wasm-bindgen]
version = "^0.2"
features = ["serde-serialize"]
"#,
);
fixture
}
pub fn wbg_test_diff_versions() -> Fixture {
let fixture = Fixture::new();
fixture
.readme()
.file(
"Cargo.toml",
r#"
[package]
name = "wbg-test-diff-versions"
version = "0.1.0"
authors = ["The wasm-pack developers"]
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
# We depend on the latest wasm-bindgen 0.2
wasm-bindgen = "0.2"
[dev-dependencies]
# And we depend on wasm-bindgen-test 0.2.29. This should still
# work, and we should end up with the latest `wasm-bindgen` and
# wasm-bindgen-test at 0.2.29, and everything should still work.
wasm-bindgen-test = "0.2.29"
"#,
)
.file(
"src/lib.rs",
r#"
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn one() -> u32 { 1 }
"#,
)
.file(
"tests/node.rs",
r#"
extern crate wbg_test_diff_versions;
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
#[wasm_bindgen_test]
fn pass() {
assert_eq!(wbg_test_diff_versions::one(), 1);
}
"#,
);
fixture
}
pub fn wbg_test_browser() -> Fixture {
let fixture = Fixture::new();
fixture
.readme()
.cargo_toml("wbg-test-browser")
.hello_world_src_lib()
.file(
"tests/browser.rs",
r#"
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);
}
"#,
);
fixture
}
pub fn wbg_test_fail() -> Fixture {
let fixture = Fixture::new();
fixture
.readme()
.cargo_toml("wbg-test-fail")
.hello_world_src_lib()
.file(
"tests/node.rs",
r#"
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1, 2);
}
"#,
);
fixture
}
pub fn wbg_test_node() -> Fixture {
let fixture = Fixture::new();
fixture
.readme()
.cargo_toml("wbg-test-node")
.hello_world_src_lib()
.file(
"tests/node.rs",
r#"
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1, 1);
}
"#,
);
fixture
}
pub fn transitive_dependencies() -> Fixture {
fn project_main_fixture(fixture: &mut Fixture) {
fixture.file(PathBuf::from("main/README"), "# Main Fixture\n");
fixture.file(
PathBuf::from("main/Cargo.toml"),
r#"
[package]
authors = ["The wasm-pack developers"]
description = "so awesome rust+wasm package"
license = "WTFPL"
name = "main_project"
repository = "https://github.com/drager/wasm-pack.git"
version = "0.1.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
project_a = { path = "../project_a" }
project_b = { path = "../project_b" }
[dev-dependencies]
wasm-bindgen-test = "0.3"
"#,
);
fixture.file(
PathBuf::from("main/src/lib.rs"),
r#"
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
// Import the `window.alert` function from the Web.
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
// Export a `greet` function from Rust to JavaScript, that alerts a
// hello message.
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
"#,
);
}
fn project_a_fixture(fixture: &mut Fixture) {
fixture.file(
PathBuf::from("project_a/README"),
"# Project Alpha Fixture\n",
);
fixture.file(
PathBuf::from("project_a/Cargo.toml"),
r#"
[package]
authors = ["The wasm-pack developers"]
description = "so awesome rust+wasm package"
license = "WTFPL"
name = "project_a"
repository = "https://github.com/drager/wasm-pack.git"
version = "0.1.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
project_b = { path = "../project_b" }
[dev-dependencies]
wasm-bindgen-test = "0.3"
"#,
);
fixture.file(
PathBuf::from("project_a/src/lib.rs"),
r#"
extern crate wasm_bindgen;
// extern crate project_b;
use wasm_bindgen::prelude::*;
// Import the `window.alert` function from the Web.
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
// Export a `greet` function from Rust to JavaScript, that alerts a
// hello message.
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
"#,
);
}
fn project_b_fixture(fixture: &mut Fixture) {
fixture.file(
PathBuf::from("project_b/README"),
"# Project Beta Fixture\n",
);
fixture.file(
PathBuf::from("project_b/Cargo.toml"),
r#"
[package]
authors = ["The wasm-pack developers"]
description = "so awesome rust+wasm package"
license = "WTFPL"
name = "project_b"
repository = "https://github.com/drager/wasm-pack.git"
version = "0.1.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
[dev-dependencies]
wasm-bindgen-test = "0.3"
"#,
);
fixture.file(
PathBuf::from("project_b/src/lib.rs"),
r#"
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
// Import the `window.alert` function from the Web.
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
// Export a `greet` function from Rust to JavaScript, that alerts a
// hello message.
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
"#,
);
}
let mut fixture = Fixture::new();
project_b_fixture(&mut fixture);
project_a_fixture(&mut fixture);
project_main_fixture(&mut fixture);
fixture
}
pub fn single_license() -> Fixture {
let fixture = Fixture::new();
fixture
.readme()
.cargo_toml("single_license")
.license()
.hello_world_src_lib();
fixture
}
pub fn dual_license() -> Fixture {
let fixture = Fixture::new();
fixture
.readme()
.cargo_toml("dual_license")
.wtfpl_license()
.mit_license()
.hello_world_src_lib();
fixture
}
pub fn non_standard_license(license_file: &str) -> Fixture {
let fixture = Fixture::new();
fixture
.readme()
.cargo_toml_with_license_file("dual_license", license_file)
.file(license_file, "license file for test")
.hello_world_src_lib();
fixture
}