測試康威生命遊戲

現在我們的 Rust 實作版本的生命遊戲已經透過 JavaScript 呈現在瀏覽器中,我們來討論一下如何測試 Rust 生成的 WebAssembly 函式。

我們將測試 tick 函式,以確定它會提供我們預期的輸出。

接下來,我們要在 wasm_game_of_life/src/lib.rs 檔案中現有的 impl Universe 區塊內建立一些 setter 和 getter 函式。我們將建立一個 set_width 和一個 set_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 的特定列和欄中設定 cellsAlive


# #![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,即可執行測試。