wasm-bindgen/tests/all/classes.rs
Alex Crichton e55af85edc
Support by-value self methods (#348)
Refactor slightly to use the same internal support that the other reference
conversions are using.

Closes #329
2018-06-28 20:09:11 -05:00

718 lines
16 KiB
Rust

use super::project;
#[test]
fn simple() {
project()
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Foo {
contents: u32,
}
#[wasm_bindgen]
impl Foo {
#[wasm_bindgen(constructor)]
pub fn new() -> Foo {
Foo::with_contents(0)
}
pub fn with_contents(a: u32) -> Foo {
Foo { contents: a }
}
pub fn add(&mut self, amt: u32) -> u32 {
self.contents += amt;
self.contents
}
pub fn consume(self) -> u32 {
self.contents
}
}
"#,
)
.file(
"test.ts",
r#"
import * as assert from "assert";
import { Foo } from "./out";
export function test() {
const r = Foo.new();
assert.strictEqual(r.add(0), 0);
assert.strictEqual(r.add(1), 1);
assert.strictEqual(r.add(1), 2);
r.add(2);
assert.strictEqual(r.consume(), 4);
assert.throws(() => r.free(), /null pointer passed to rust/);
const r2 = Foo.with_contents(10);
assert.strictEqual(r2.add(1), 11);
assert.strictEqual(r2.add(2), 13);
assert.strictEqual(r2.add(3), 16);
r2.free();
const r3 = new Foo();
assert.strictEqual(r3.add(42), 42);
r3.free();
}
"#,
)
.test();
}
#[test]
fn strings() {
project()
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Foo {
name: u32,
}
#[wasm_bindgen]
pub struct Bar {
contents: String,
}
#[wasm_bindgen]
impl Foo {
pub fn new() -> Foo {
Foo { name: 0 }
}
pub fn set(&mut self, amt: u32) {
self.name = amt;
}
pub fn bar(&self, mix: &str) -> Bar {
Bar { contents: format!("foo-{}-{}", mix, self.name) }
}
}
#[wasm_bindgen]
impl Bar {
pub fn name(&self) -> String {
self.contents.clone()
}
}
"#,
)
.file(
"test.ts",
r#"
import * as assert from "assert";
import { Foo } from "./out";
export function test() {
const r = Foo.new();
r.set(3);
let bar = r.bar('baz');
r.free();
assert.strictEqual(bar.name(), "foo-baz-3");
bar.free();
}
"#,
)
.test();
}
#[test]
fn exceptions() {
project()
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct A {
}
#[wasm_bindgen]
impl A {
pub fn new() -> A {
A {}
}
pub fn foo(&self, _: &A) {
}
pub fn bar(&mut self, _: &mut A) {
}
}
#[wasm_bindgen]
pub struct B {
}
#[wasm_bindgen]
impl B {
pub fn new() -> B {
B {}
}
}
"#,
)
.file(
"test.js",
r#"
import * as assert from "assert";
import { A, B } from "./out";
export function test() {
assert.throws(() => new A(), /cannot invoke `new` directly/);
let a = A.new();
a.free();
assert.throws(() => a.free(), /null pointer passed to rust/);
let b = A.new();
b.foo(b);
assert.throws(() => b.bar(b), /recursive use of an object/);
let c = A.new();
let d = B.new();
assert.throws(() => c.foo(d), /expected instance of A/);
d.free();
c.free();
};
"#,
)
.file(
"test.d.ts",
r#"
export function test(): void;
"#,
)
.test();
}
#[test]
fn pass_one_to_another() {
project()
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct A {}
#[wasm_bindgen]
impl A {
pub fn new() -> A {
A {}
}
pub fn foo(&self, _other: &B) {
}
pub fn bar(&self, _other: B) {
}
}
#[wasm_bindgen]
pub struct B {}
#[wasm_bindgen]
impl B {
pub fn new() -> B {
B {}
}
}
"#,
)
.file(
"test.ts",
r#"
import { A, B } from "./out";
export function test() {
let a = A.new();
let b = B.new();
a.foo(b);
a.bar(b);
a.free();
}
"#,
)
.test();
}
#[test]
fn pass_into_js() {
project()
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Foo(i32);
#[wasm_bindgen]
impl Foo {
pub fn inner(&self) -> i32 {
self.0
}
}
#[wasm_bindgen(module = "./test")]
extern {
fn take_foo(foo: Foo);
}
#[wasm_bindgen]
pub fn run() {
take_foo(Foo(13));
}
"#,
)
.file(
"test.ts",
r#"
import { run, Foo } from "./out";
import * as assert from "assert";
export function take_foo(foo: Foo) {
assert.strictEqual(foo.inner(), 13);
foo.free();
assert.throws(() => foo.free(), /null pointer passed to rust/);
}
export function test() {
run();
}
"#,
)
.test();
}
#[test]
fn issue_27() {
project()
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Context {}
#[wasm_bindgen]
impl Context {
pub fn parse(&self, _expr: &str) -> Expr {
panic!()
}
pub fn eval(&self, _expr: &Expr) -> f64 {
panic!()
}
pub fn set(&mut self, _var: &str, _val: f64) {
panic!()
}
}
#[wasm_bindgen]
pub struct Expr {}
#[wasm_bindgen]
pub fn context() -> Context {
Context {}
}
"#,
)
.file(
"test.ts",
r#"
import { context } from "./out";
export function test() {
context();
}
"#,
)
.test();
}
#[test]
fn pass_into_js_as_js_class() {
project()
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Foo(i32);
#[wasm_bindgen]
impl Foo {
pub fn inner(&self) -> i32 {
self.0
}
}
#[wasm_bindgen(module = "./test")]
extern {
fn take_foo(foo: JsValue);
}
#[wasm_bindgen]
pub fn run() {
take_foo(Foo(13).into());
}
"#,
)
.file(
"test.ts",
r#"
import { run, Foo } from "./out";
import * as assert from "assert";
export function take_foo(foo: any) {
assert(foo instanceof Foo);
assert.strictEqual(foo.inner(), 13);
foo.free();
}
export function test() {
run();
}
"#,
)
.test();
}
#[test]
fn constructors() {
project()
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn cross_item_construction() -> Bar {
Bar::other_name(7, 8)
}
#[wasm_bindgen]
pub struct Foo {
number: u32,
}
#[wasm_bindgen]
impl Foo {
#[wasm_bindgen(constructor)]
pub fn new(number: u32) -> Foo {
Foo { number }
}
pub fn get_number(&self) -> u32 {
self.number
}
}
#[wasm_bindgen]
pub struct Bar {
number: u32,
number2: u32,
}
#[wasm_bindgen]
impl Bar {
#[wasm_bindgen(constructor)]
pub fn other_name(number: u32, number2: u32) -> Bar {
Bar { number, number2 }
}
pub fn get_sum(&self) -> u32 {
self.number + self.number2
}
}
"#,
)
.file(
"test.ts",
r#"
import * as assert from "assert";
import { Foo, Bar, cross_item_construction } from "./out";
export function test() {
const foo = new Foo(1);
assert.strictEqual(foo.get_number(), 1);
foo.free();
const foo2 = Foo.new(2);
assert.strictEqual(foo2.get_number(), 2);
foo2.free();
const bar = new Bar(3, 4);
assert.strictEqual(bar.get_sum(), 7);
bar.free();
const bar2 = Bar.other_name(5, 6);
assert.strictEqual(bar2.get_sum(), 11);
bar2.free();
assert.strictEqual(cross_item_construction().get_sum(), 15);
}
"#,
)
.test();
}
#[test]
fn empty_structs() {
project()
.debug(false)
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct MissingClass {}
#[wasm_bindgen]
pub struct Other {}
#[wasm_bindgen]
impl Other { pub fn return_a_value() -> MissingClass { MissingClass {} } }
"#,
)
.file(
"test.ts",
r#"
import { Other } from "./out";
export function test() {
Other.return_a_value();
}
"#,
)
.test();
}
#[test]
fn public_fields() {
project()
.debug(false)
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Default)]
pub struct Foo {
pub a: u32,
pub b: f32,
pub c: f64,
pub d: i32,
}
#[wasm_bindgen]
impl Foo {
pub fn new() -> Foo {
Foo::default()
}
}
"#,
)
.file(
"test.ts",
r#"
import { Foo } from "./out";
import * as assert from "assert";
export function test() {
const a = Foo.new();
assert.strictEqual(a.a, 0);
a.a = 3;
assert.strictEqual(a.a, 3);
assert.strictEqual(a.b, 0);
a.b = 7;
assert.strictEqual(a.b, 7);
assert.strictEqual(a.c, 0);
a.c = 8;
assert.strictEqual(a.c, 8);
assert.strictEqual(a.d, 0);
a.d = 3.3;
assert.strictEqual(a.d, 3);
}
"#,
)
.test();
}
#[test]
fn using_self() {
project()
.debug(false)
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Foo {
}
#[wasm_bindgen]
impl Foo {
pub fn new() -> Self {
Foo {}
}
}
"#,
)
.file(
"test.ts",
r#"
import { Foo } from "./out";
export function test() {
Foo.new().free();
}
"#,
)
.test();
}
#[test]
fn readonly_fields() {
project()
.debug(false)
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Default)]
pub struct Foo {
#[wasm_bindgen(readonly)]
pub a: u32,
}
#[wasm_bindgen]
impl Foo {
pub fn new() -> Foo {
Foo::default()
}
}
"#,
)
.file(
"test.ts",
r#"
import { Foo } from "./out";
import * as assert from "assert";
export function test() {
const a = Foo.new();
assert.strictEqual(a.a, 0);
assert.throws(() => (a as any).a = 3, /has only a getter/);
a.free();
}
"#,
)
.test();
}
#[test]
fn double_consume() {
project()
.file("src/lib.rs", r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Foo { }
#[wasm_bindgen]
impl Foo {
#[wasm_bindgen(constructor)]
pub fn new() -> Foo {
Foo {}
}
pub fn consume(self, other: Foo) {
drop(other);
}
}
"#)
.file("test.ts", r#"
import * as assert from "assert";
import { Foo } from "./out";
export function test() {
const r = Foo.new();
assert.throws(() => r.consume(r), /Attempt to use a moved value/);
}
"#)
.test();
}