实现康威的生命游戏

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

我们将测试我们的 tick 函数以确保它提供我们期望的输出。

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


#![allow(unused)]
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 文件中创建另一个不带 #[wasm_bindgen] 属性的 impl Universe 块。 有一些我们需要测试的函数我们不想暴露给我们的 JavaScript。 Rust 生成的 WebAssembly 函数不能返回借用的引用。 尝试使用属性编译 Rust 生成的 WebAssembly 并查看您得到的错误。

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


#![allow(unused)]
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 crate 和 Universe 类型。


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

wasm_game_of_life/tests/web.rs 文件中,我们将要创建一些飞船建造器功能。

我们需要一个用于我们的输入宇宙飞船,我们将调用 tick 函数,我们希望在一个刻度后我们将获得预期的宇宙飞船。 我们选择了要初始化为 Alive 的单元格,以在 input_spaceship 函数中创建我们的飞船。 input_spaceship 勾选后,expected_spaceship 函数中飞船的位置是手动计算的。 您可以自己确认一下,输入飞船的单元格在一个tick后与预期的飞船相同。


#![allow(unused)]
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)]
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 进行测试。