Skip to content

Commit

Permalink
Start implementing a test suite for the CLI
Browse files Browse the repository at this point in the history
We have very few tests today so this starts to add the basics of a test
suite which compiles Cargo projects on-the-fly which will hopefully help
us bolster the amount of assertions we can make about the output.
  • Loading branch information
alexcrichton committed Mar 22, 2019
1 parent f3b190a commit dfaed38
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 1 deletion.
4 changes: 4 additions & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,9 @@ walrus = "0.5"
wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.40" }
wasm-bindgen-shared = { path = "../shared", version = "=0.2.40" }

[dev-dependencies]
assert_cmd = "0.11"
predicates = "1.0.0"

[features]
vendored-openssl = ['openssl/vendored']
2 changes: 1 addition & 1 deletion crates/cli/src/bin/wasm-bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ fn main() {
};
eprintln!("error: {}", err);
for cause in err.iter_causes() {
eprintln!("\tcaused by: {}", cause);
eprintln!(" caused by: {}", cause);
}
process::exit(1);
}
Expand Down
138 changes: 138 additions & 0 deletions crates/cli/tests/wasm-bindgen/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//! A small test suite for the `wasm-bindgen` CLI command itself
//!
//! This test suite is intended to exercise functionality of the CLI in terms of
//! errors and such. It is not intended for comprehensive behavior testing, as
//! that should all be placed in the top-level `tests` directory for the
//! `wasm-bindgen` crate itself.
//!
//! Assertions about errors in `wasm-bindgen` or assertions about the output of
//! `wasm-bindgen` should all be placed in this test suite, however. Currently
//! it is largely based off actually running `cargo build` at test time which is
//! quite expensive, so it's recommended that this test suite doesn't become too
//! large!

use assert_cmd::prelude::*;
use predicates::str;
use std::env;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

fn target_dir() -> PathBuf {
let mut dir = PathBuf::from(env::current_exe().unwrap());
dir.pop(); // current exe
if dir.ends_with("deps") {
dir.pop();
}
dir.pop(); // debug and/or release
return dir;
}

fn repo_root() -> PathBuf {
let mut repo_root = env::current_dir().unwrap();
repo_root.pop(); // remove 'cli'
repo_root.pop(); // remove 'crates'
repo_root
}

struct Project {
root: PathBuf,
name: &'static str,
}

impl Project {
fn new(name: &'static str) -> Project {
let root = target_dir().join("cli-tests").join(name);
drop(fs::remove_dir_all(&root));
fs::create_dir_all(&root).unwrap();
Project { root, name }
}

fn file(&mut self, name: &str, contents: &str) -> &mut Project {
let dst = self.root.join(name);
fs::create_dir_all(dst.parent().unwrap()).unwrap();
fs::write(&dst, contents).unwrap();
self
}

fn wasm_bindgen(&mut self, args: &str) -> (Command, PathBuf) {
let wasm = self.build();
let output = self.root.join("pkg");
fs::create_dir_all(&output).unwrap();
let mut cmd = Command::cargo_bin("wasm-bindgen").unwrap();
cmd.arg("--out-dir").arg(&output);
cmd.arg(&wasm);
for arg in args.split_whitespace() {
cmd.arg(arg);
}
(cmd, output)
}

fn build(&mut self) -> PathBuf {
if !self.root.join("Cargo.toml").is_file() {
self.file(
"Cargo.toml",
&format!(
"
[package]
name = \"{}\"
authors = []
version = \"1.0.0\"
edition = '2018'
[dependencies]
wasm-bindgen = {{ path = '{}' }}
[lib]
crate-type = ['cdylib']
[workspace]
",
self.name,
repo_root().display(),
),
);
}

let target_dir = target_dir();
Command::new("cargo")
.current_dir(&self.root)
.arg("build")
.arg("--target")
.arg("wasm32-unknown-unknown")
.env("CARGO_TARGET_DIR", &target_dir)
.assert()
.success();

target_dir
.join("wasm32-unknown-unknown")
.join("debug")
.join(self.name)
.with_extension("wasm")
}
}

#[test]
fn version_useful() {
Command::cargo_bin("wasm-bindgen")
.unwrap()
.arg("-V")
.assert()
.stdout(str::ends_with("\n"))
.stdout(str::starts_with("wasm-bindgen "))
.success();
}

#[test]
fn works_on_empty_project() {
let (mut cmd, _out_dir) = Project::new("works_on_empty_project")
.file(
"src/lib.rs",
r#"
"#,
)
.wasm_bindgen("");
cmd.assert().success();
}

mod npm;
155 changes: 155 additions & 0 deletions crates/cli/tests/wasm-bindgen/npm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
use crate::*;

#[test]
fn no_modules_rejects_npm() {
let (mut cmd, _out_dir) = Project::new("no_modules_rejects_npm")
.file(
"src/lib.rs",
r#"
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "foo")]
extern {
fn foo();
}
#[wasm_bindgen(start)]
pub fn main() {
foo();
}
"#,
)
.file("package.json", "")
.wasm_bindgen("--no-modules");
cmd.assert()
.stderr("\
error: failed to generate bindings for JS import `foo`
caused by: import from `foo` module not allowed with `--no-modules`; use `--nodejs`, `--browser`, or no flag instead
")
.failure();
}

#[test]
fn more_package_json_fields_rejected() {
let (mut cmd, _out_dir) = Project::new("more_package_json_fields_rejected")
.file(
"src/lib.rs",
r#"
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "foo")]
extern {
fn foo();
}
#[wasm_bindgen(start)]
pub fn main() {
foo();
}
"#,
)
.file(
"package.json",
r#"
{
"name": "foo",
"dependencies": {}
}
"#,
)
.wasm_bindgen("");
cmd.assert()
.stderr(str::is_match("\
error: NPM manifest found at `.*` can currently only have one key, .*
").unwrap())
.failure();
}

#[test]
fn npm_conflict_rejected() {
let (mut cmd, _out_dir) = Project::new("npm_conflict_rejected")
.file(
"Cargo.toml",
&format!(r#"
[package]
name = "npm_conflict_rejected"
authors = []
version = "1.0.0"
edition = '2018'
[dependencies]
wasm-bindgen = {{ path = '{}' }}
bar = {{ path = 'bar' }}
[lib]
crate-type = ['cdylib']
[workspace]
"#,
repo_root().display()
)
)
.file(
"src/lib.rs",
r#"
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "bar")]
extern {
fn foo();
}
#[wasm_bindgen(start)]
pub fn main() {
foo();
bar::foo();
}
"#,
)
.file(
"package.json",
r#"
{
"dependencies": {"bar": "0.0.0"}
}
"#,
)
.file(
"bar/Cargo.toml",
&format!(r#"
[package]
name = "bar"
authors = []
version = "1.0.0"
edition = '2018'
[dependencies]
wasm-bindgen = {{ path = '{}' }}
"#,
repo_root().display()
)
)
.file(
"bar/src/lib.rs",
r#"
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "bar")]
extern {
pub fn foo();
}
"#,
)
.file(
"bar/package.json",
r#"
{
"dependencies": {"bar": "1.0.0"}
}
"#,
)
.wasm_bindgen("");
cmd.assert()
.stderr(str::is_match("dependency on NPM package `bar` specified in two").unwrap())
.failure();
}

0 comments on commit dfaed38

Please sign in to comment.