这是使用 Rust 和 WebAssembly 的未发布文档,已发布的文档可在 Rust 和 WebAssembly 主文档网站上找到 。此处记录的功能可能在 Rust 和 WebAssembly 的已发布工具版本中不可用。

测试康威的生命游戏

现在我们已经有了在浏览器中使用 JavaScript 渲染的生命游戏 Rust 实现,让我们来谈谈如何测试我们用 Rust 生成的 WebAssembly 函数。

我们将测试我们的 tick 函数,以确保它能给出我们期望的输出。

接下来,我们想在 wasm_game_of_life/src/lib.rs 文件中现有的 impl Universe 块内创建一些 setter 和 getter 函数。我们将创建一个 set_widthset_height 函数,以便我们可以创建不同大小的 Universe


# #![allow(unused_variables)]
#fn main() {
#[wasm_bindgen]
impl Universe { 
    // ...

    /// Set the width of the universe.
    ///
    /// Resets all cells to the dead state.
    pub fn set_width(&mut self, width: u32) {
        self.width = width;
        self.cells = (0..width * self.height).map(|_i| Cell::Dead).collect();
    }

    /// Set the height of the universe.
    ///
    /// Resets all cells to the dead state.
    pub fn set_height(&mut self, height: u32) {
        self.height = height;
        self.cells = (0..self.width * height).map(|_i| Cell::Dead).collect();
    }

}
#}

我们将在 wasm_game_of_life/src/lib.rs 文件中创建另一个 impl Universe 块,但没有 #[wasm_bindgen] 属性。我们有一些用于测试的函数,我们不想将其暴露给 JavaScript。用 Rust 生成的 WebAssembly 函数不能返回借用引用。尝试用该属性编译用 Rust 生成的 WebAssembly,并查看你得到的错误。

我们将编写 get_cells 的实现,以获取 Universecells 内容。我们还将编写一个 set_cells 函数,以便我们可以将 Universe 中特定行和列的 cells 设置为 Alive


# #![allow(unused_variables)]
#fn main() {
impl Universe {
    /// Get the dead and alive values of the entire universe.
    pub fn get_cells(&self) -> &[Cell] {
        &self.cells
    }

    /// Set cells to be alive in a universe by passing the row and column
    /// of each cell as an array.
    pub fn set_cells(&mut self, cells: &[(u32, u32)]) {
        for (row, col) in cells.iter().cloned() {
            let idx = self.get_index(row, col);
            self.cells[idx] = Cell::Alive;
        }
    }

}
#}

现在我们将创建 wasm_game_of_life/tests/web.rs 文件中的测试。

在我们这样做之前,该文件中已经有一个可用的测试。你可以通过在 wasm-game-of-life 目录中运行 wasm-pack test --chrome --headless 来确认用 Rust 生成的 WebAssembly 测试是否有效。你也可以使用 --firefox--safari--node 选项在这些浏览器中测试你的代码。

wasm_game_of_life/tests/web.rs 文件中,我们需要导出我们的 wasm_game_of_life 箱子和 Universe 类型。


# #![allow(unused_variables)]
#fn main() {
extern crate wasm_game_of_life;
use wasm_game_of_life::Universe;
#}

wasm_game_of_life/tests/web.rs 文件中,我们想创建一些宇宙飞船构建器函数。

我们想要一个用于我们将在其上调用 tick 函数的输入宇宙飞船,以及我们希望在一次 tick 后得到的预期宇宙飞船。我们在 input_spaceship 函数中选择了我们想要初始化为 Alive 的单元格来创建我们的宇宙飞船。input_spaceship 一次 tick 后 expected_spaceship 函数中宇宙飞船的位置是手动计算的。你可以自己确认输入宇宙飞船的单元格在一次 tick 后是否与预期宇宙飞船相同。


# #![allow(unused_variables)]
#fn main() {
#[cfg(test)]
pub fn input_spaceship() -> Universe {
    let mut universe = Universe::new();
    universe.set_width(6);
    universe.set_height(6);
    universe.set_cells(&[(1,2), (2,3), (3,1), (3,2), (3,3)]);
    universe
}

#[cfg(test)]
pub fn expected_spaceship() -> Universe {
    let mut universe = Universe::new();
    universe.set_width(6);
    universe.set_height(6);
    universe.set_cells(&[(2,1), (2,3), (3,2), (3,3), (4,2)]);
    universe
}
#}

现在我们将编写 test_tick 函数的实现。首先,我们创建 input_spaceship()expected_spaceship() 的实例。然后,我们在 input_universe 上调用 tick。最后,我们使用 assert_eq! 宏调用 get_cells() 来确保 input_universeexpected_universe 具有相同的 Cell 数组值。我们在代码块中添加 #[wasm_bindgen_test] 属性,以便我们可以测试用 Rust 生成的 WebAssembly 代码,并使用 wasm-pack test 测试 WebAssembly 代码。


# #![allow(unused_variables)]
#fn main() {
#[wasm_bindgen_test]
pub fn test_tick() {
    // Let's create a smaller Universe with a small spaceship to test!
    let mut input_universe = input_spaceship();

    // This is what our spaceship should look like
    // after one tick in our universe.
    let expected_universe = expected_spaceship();

    // Call `tick` and then see if the cells in the `Universe`s are the same.
    input_universe.tick();
    assert_eq!(&input_universe.get_cells(), &expected_universe.get_cells());
}
#}

通过在 wasm-game-of-life 目录中运行 wasm-pack test --firefox --headless 来运行测试。