From a8705f518ac25147188ffbc321af363c2399519d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20S=C3=A1nchez=20de=20Le=C3=B3n=20Peque?= Date: Tue, 20 Mar 2018 20:32:15 +0100 Subject: [PATCH] Add some tests and Travis integration --- .gitignore | 2 + .travis.yml | 10 ++++ classic/.gitignore | 0 halfsize/.gitignore | 0 test_mazes.py | 125 ++++++++++++++++++++++++++++++++++++++++++++ training/.gitignore | 0 6 files changed, 137 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml delete mode 100644 classic/.gitignore delete mode 100644 halfsize/.gitignore create mode 100644 test_mazes.py delete mode 100644 training/.gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6af855 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.pytest_cache/ +__pycache__/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9a17827 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: python + +python: + - 3.6 + +install: + - pip install pytest pytest-pep8 pytest-flakes + +script: + - pytest --pep8 --flakes diff --git a/classic/.gitignore b/classic/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/halfsize/.gitignore b/halfsize/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/test_mazes.py b/test_mazes.py new file mode 100644 index 0000000..2caaf85 --- /dev/null +++ b/test_mazes.py @@ -0,0 +1,125 @@ +""" +Basic tests to ensure maze files are properly formatted. +""" +from pathlib import Path +import re + +import pytest + + +def parametrize_maze_files(maze_files): + return pytest.mark.parametrize('maze_file', maze_files, + ids=[str(x) for x in maze_files]) + + +CLASSIC_SIZE = 16 +HALFSIZE_SIZE = 32 + +classic_mazes = list(Path('classic').glob('*')) +halfsize_mazes = list(Path('halfsize').glob('*')) +training_mazes = list(Path('training').glob('*')) +competition_mazes = classic_mazes + halfsize_mazes +all_mazes = classic_mazes + halfsize_mazes + training_mazes + +classic_mazes = parametrize_maze_files(classic_mazes) +halfsize_mazes = parametrize_maze_files(halfsize_mazes) +training_mazes = parametrize_maze_files(training_mazes) +competition_mazes = parametrize_maze_files(competition_mazes) +all_mazes = parametrize_maze_files(all_mazes) + + +def read_maze(maze_file): + rows = maze_file.read_text().split('\n') + for i, row in enumerate(rows): + if not row.startswith(('o', '|')): + break + return rows[:i] + + +@all_mazes +def test_file_name(maze_file): + """ + - All maze files must be text files with `.txt` extension. + - Only "a-z", "A-Z", "0-9", "-" and "_" characters are allowed. + """ + assert maze_file.suffix == '.txt' + assert re.match('^[a-zA-Z0-9\-_]*$', maze_file.stem) + + +@classic_mazes +def test_classic_size(maze_file): + """ + Classic maze size is always 16x16. + """ + rows = read_maze(maze_file) + assert len(rows) == (CLASSIC_SIZE * 2 + 1) + assert all(len(row) == (CLASSIC_SIZE * 4 + 1) for row in rows) + + +@halfsize_mazes +def test_halfsize_size(maze_file): + """ + Half-size maze size is always 32x32. + """ + rows = read_maze(maze_file) + assert len(rows) == (HALFSIZE_SIZE * 2 + 1) + assert all(len(row) == (HALFSIZE_SIZE * 4 + 1) for row in rows) + + +@training_mazes +def test_training_size(maze_file): + """ + Training maze size is the same as classic size, even if not fully used. + """ + rows = read_maze(maze_file) + assert len(rows) == (CLASSIC_SIZE * 2 + 1) + assert all(len(row) == (CLASSIC_SIZE * 4 + 1) for row in rows) + + +@all_mazes +def test_format(maze_file): + """ + Maze file format must be homogeneous. Vertical walls are represented with + a `|`, horizontal walls with `---` and posts with `o`. + + Even rows must: + + - Have a post each 4 characters. + - Have a wall `---` or nothing ` ` between posts. + + Odd rows must: + + - Have a wall `|` or nothing ` ` each 4 characters. + """ + rows = read_maze(maze_file) + even_rows = rows[::2] + assert all(all(x == 'o' for x in row[::4]) for row in even_rows) + assert all(all(x in ('---', ' ') for x in row.strip('o').split('o')) + for row in even_rows) + odd_rows = rows[1::2] + assert all(all(x in ('|', ' ') for x in row[::4]) for row in odd_rows) + + +@all_mazes +def test_boundaries(maze_file): + """ + Maze boundaries are expected to be closed. + """ + rows = read_maze(maze_file) + east_boundary = [row[-1] for row in rows[1::2]] + west_boundary = [row[0] for row in rows[1::2]] + assert all(v == '|' for v in east_boundary + west_boundary) + north_boundary = rows[0].strip('o').split('o') + south_boundary = rows[-1].strip('o').split('o') + assert all(h == '---' for h in north_boundary + south_boundary) + + +@competition_mazes +def test_starting_cell(maze_file): + """ + The starting cell is expected to be at the South-West and facing North. + """ + rows = read_maze(maze_file) + assert rows[-3][:5] == 'o o' + assert rows[-2][:5] == '| |' + assert rows[-1][:5] == 'o---o' diff --git a/training/.gitignore b/training/.gitignore deleted file mode 100644 index e69de29..0000000