Testing Conway's Game of Life

Now that we have our Rust implementation of the Game of Life rendering in thebrowser with JavaScript, let's talk about testing our Rust-generatedWebAssembly functions.

We are going to test our tick function to make sure that it gives us theoutput that we expect.

Next, we'll want to create some setter and getterfunctions inside our existing impl Universe block in thewasm_game_of_life/src/lib.rs file. We are going to create a set_widthand a set_height function so we can create Universes of different sizes.

  1. # #![allow(unused_variables)]
  2. #fn main() {
  3. #[wasm_bindgen]
  4. impl Universe {
  5. // ...
  6. /// Set the width of the universe.
  7. ///
  8. /// Resets all cells to the dead state.
  9. pub fn set_width(&mut self, width: u32) {
  10. self.width = width;
  11. self.cells = (0..width * self.height).map(|_i| Cell::Dead).collect();
  12. }
  13. /// Set the height of the universe.
  14. ///
  15. /// Resets all cells to the dead state.
  16. pub fn set_height(&mut self, height: u32) {
  17. self.height = height;
  18. self.cells = (0..self.width * height).map(|_i| Cell::Dead).collect();
  19. }
  20. }
  21. #}

We are going to create another impl Universe block inside ourwasm_game_of_life/src/lib.rs file without the #[wasm_bindgen] attribute.There are a few functions we need for testing that we don't want to expose toour JavaScript. Rust-generated WebAssembly functions cannot returnborrowed references. Try compiling the Rust-generated WebAssembly with theattribute and take a look at the errors you get.

We are going to write the implementation of get_cells to get the contents ofthe cells of a Universe. We'll also write a set_cells function so we canset cells in a specific row and column of a Universe to be Alive.

  1. # #![allow(unused_variables)]
  2. #fn main() {
  3. impl Universe {
  4. /// Get the dead and alive values of the entire universe.
  5. pub fn get_cells(&self) -> &[Cell] {
  6. &self.cells
  7. }
  8. /// Set cells to be alive in a universe by passing the row and column
  9. /// of each cell as an array.
  10. pub fn set_cells(&mut self, cells: &[(u32, u32)]) {
  11. for (row, col) in cells.iter().cloned() {
  12. let idx = self.get_index(row, col);
  13. self.cells[idx] = Cell::Alive;
  14. }
  15. }
  16. }
  17. #}

Now we're going to create our test in the wasm_game_of_life/tests/web.rs file.

Before we do that, there is already one working test in the file. You canconfirm that the Rust-generated WebAssembly test is working by runningwasm-pack test —chrome —headless in the wasm-game-of-life directory.You can also use the —firefox, —safari, and —node options totest your code in those browsers.

In the wasm_game_of_life/tests/web.rs file, we need to export ourwasm_game_of_life crate and the Universe type.

  1. # #![allow(unused_variables)]
  2. #fn main() {
  3. extern crate wasm_game_of_life;
  4. use wasm_game_of_life::Universe;
  5. #}

In the wasm_game_of_life/tests/web.rs file we'll want to create somespaceship builder functions.

We'll want one for our input spaceship that we'll call the tick function onand we'll want the expected spaceship we will get after one tick. We picked thecells that we want to initialize as Alive to create our spaceship in theinput_spaceship function. The position of the spaceship in theexpected_spaceship function after the tick of the input_spaceship wascalculated manually. You can confirm for yourself that the cells of the inputspaceship after one tick is the same as the expected spaceship.

  1. # #![allow(unused_variables)]
  2. #fn main() {
  3. #[cfg(test)]
  4. pub fn input_spaceship() -> Universe {
  5. let mut universe = Universe::new();
  6. universe.set_width(6);
  7. universe.set_height(6);
  8. universe.set_cells(&[(1,2), (2,3), (3,1), (3,2), (3,3)]);
  9. universe
  10. }
  11. #[cfg(test)]
  12. pub fn expected_spaceship() -> Universe {
  13. let mut universe = Universe::new();
  14. universe.set_width(6);
  15. universe.set_height(6);
  16. universe.set_cells(&[(2,1), (2,3), (3,2), (3,3), (4,2)]);
  17. universe
  18. }
  19. #}

Now we will write the implementation for our test_tick function. First, wecreate an instance of our input_spaceship() and our expected_spaceship().Then, we call tick on the input_universe. Finally, we use the assert_eq!macro to call get_cells() to ensure that input_universe andexpected_universe have the same Cell array values. We add the#[wasm_bindgen_test] attribute to our code block so we can test ourRust-generated WebAssembly code and use wasm-pack test to test theWebAssembly code.

  1. # #![allow(unused_variables)]
  2. #fn main() {
  3. #[wasm_bindgen_test]
  4. pub fn test_tick() {
  5. // Let's create a smaller Universe with a small spaceship to test!
  6. let mut input_universe = input_spaceship();
  7. // This is what our spaceship should look like
  8. // after one tick in our universe.
  9. let expected_universe = expected_spaceship();
  10. // Call `tick` and then see if the cells in the `Universe`s are the same.
  11. input_universe.tick();
  12. assert_eq!(&input_universe.get_cells(), &expected_universe.get_cells());
  13. }
  14. #}

Run the tests within the wasm-game-of-life directory by runningwasm-pack test —firefox —headless.