From f0b74f3a5c4bbf838754f67bb717f483948d32fe Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Sat, 12 Sep 2020 20:36:17 +0900 Subject: [PATCH 1/9] Add pytest and tests for dsu (#5) --- requirements.txt | 8 +++++ tests/__init__.py | 0 tests/conftest.py | 8 +++++ tests/test_dsu.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 requirements.txt create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_dsu.py diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7d3a2bc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +autopep8 +flake8 +numpy == 1.18.2 +pep8-naming +pyflakes +pytest +pytest-watch +scipy == 1.4.1 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..8395053 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,8 @@ +import os +import sys + +# Add path. +# See: +# https://www.magata.net/memo/index.php?pytest%C6%FE%CC%E7#y2046859 +path = os.path.dirname(os.path.abspath(__file__)) + '/../atcoder/' +sys.path.append(os.path.abspath(path)) diff --git a/tests/test_dsu.py b/tests/test_dsu.py new file mode 100644 index 0000000..68cc029 --- /dev/null +++ b/tests/test_dsu.py @@ -0,0 +1,79 @@ +import pytest + +from atcoder.dsu import DSU + + +@pytest.fixture +def dsu(): + return DSU(5) + + +class TestDsu(object): + + def test_merge(self, dsu): + ''' + dsu.merge(vertex a, vertex b) is expected to be in the same group. + + GIVEN an initialized dsu object + WHEN vertex 0 and 1 are merged + THEN vertex 0 and 1 are the same group. + ''' + + is_same = dsu.same(0, 1) + assert is_same is False + + dsu.merge(0, 1) + is_same = dsu.same(0, 1) + assert is_same is True + + def test_size(self, dsu): + ''' + dsu.size(vertex a) is expected to get size of vertex a. + + GIVEN an initialized dsu object + WHEN vertex 0, 1 and 2 are merged + THEN size of vertex 0 is 3. + ''' + + dsu.merge(0, 1) + dsu.merge(0, 2) + assert dsu.size(0) == 3 + + is_same = dsu.same(0, 3) + assert is_same is False + + is_same = dsu.same(0, 4) + assert is_same is False + + def test_leader(self, dsu): + ''' + dsu.leader(vertex a) is expected to return the representative of the + connected component that contains the vertex a. + + GIVEN an initialized dsu object + WHEN vertex 0, 1 and 2 are merged + THEN vertex 1 and 2 belong to vertex 0. + ''' + + dsu.merge(0, 1) + dsu.merge(0, 2) + + assert dsu.leader(1) == 0 + assert dsu.leader(2) == 0 + assert dsu.leader(3) != 0 + assert dsu.leader(4) != 0 + + def test_groups(self, dsu): + ''' + dsu.groups() is expected to return the list of the graph that divided + into connected components. + + GIVEN an initialized dsu object + WHEN vertex 0, 1 and 2 are merged + THEN returns [[0, 1, 2], [3], [4]] + ''' + + dsu.merge(0, 1) + dsu.merge(0, 2) + + assert dsu.groups() == [[0, 1, 2], [3], [4]] From 3b4be7575c3281a658a8ef7b26ebe5bb1a895a64 Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Sun, 13 Sep 2020 07:16:31 +0900 Subject: [PATCH 2/9] Enable to run Pytest in GitHub Actions --- .github/workflows/python-package.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index cb8835b..533f7da 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -34,6 +34,6 @@ jobs: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - # - name: Test with pytest - # run: | - # pytest + - name: Test with pytest + run: | + pytest From acf2601791a8d56302ce3e647c0dcea9796a49e4 Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Sun, 13 Sep 2020 07:17:53 +0900 Subject: [PATCH 3/9] Remove requirements.txt --- requirements.txt | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7d3a2bc..0000000 --- a/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -autopep8 -flake8 -numpy == 1.18.2 -pep8-naming -pyflakes -pytest -pytest-watch -scipy == 1.4.1 From f72510097f13b66c0c5d92777a801e4bd1059f4a Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Sun, 13 Sep 2020 08:37:18 +0900 Subject: [PATCH 4/9] Reflect review of existing dsu's tests --- tests/test_dsu.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/tests/test_dsu.py b/tests/test_dsu.py index 68cc029..c9c842a 100644 --- a/tests/test_dsu.py +++ b/tests/test_dsu.py @@ -8,7 +8,7 @@ def dsu(): return DSU(5) -class TestDsu(object): +class TestDsu: def test_merge(self, dsu): ''' @@ -20,11 +20,11 @@ def test_merge(self, dsu): ''' is_same = dsu.same(0, 1) - assert is_same is False + assert not is_same dsu.merge(0, 1) is_same = dsu.same(0, 1) - assert is_same is True + assert is_same def test_size(self, dsu): ''' @@ -39,12 +39,6 @@ def test_size(self, dsu): dsu.merge(0, 2) assert dsu.size(0) == 3 - is_same = dsu.same(0, 3) - assert is_same is False - - is_same = dsu.same(0, 4) - assert is_same is False - def test_leader(self, dsu): ''' dsu.leader(vertex a) is expected to return the representative of the @@ -52,16 +46,16 @@ def test_leader(self, dsu): GIVEN an initialized dsu object WHEN vertex 0, 1 and 2 are merged - THEN vertex 1 and 2 belong to vertex 0. + THEN vertex 1 and 2 belong to any of the vertices 0-2. ''' dsu.merge(0, 1) dsu.merge(0, 2) - assert dsu.leader(1) == 0 - assert dsu.leader(2) == 0 - assert dsu.leader(3) != 0 - assert dsu.leader(4) != 0 + assert dsu.leader(1) in [0, 1, 2] + assert dsu.leader(2) in [0, 1, 2] + assert dsu.leader(3) not in [0, 1, 2] + assert dsu.leader(4) not in [0, 1, 2] def test_groups(self, dsu): ''' From a8451abbc49c91e730bdc4c64ce4c06b2b1f5d14 Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Sun, 13 Sep 2020 11:15:39 +0900 Subject: [PATCH 5/9] Add tests --- tests/test_dsu.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/test_dsu.py b/tests/test_dsu.py index c9c842a..de4ecea 100644 --- a/tests/test_dsu.py +++ b/tests/test_dsu.py @@ -10,6 +10,33 @@ def dsu(): class TestDsu: + def test_initial_status(self, dsu): + ''' + An initialized dsu object is expected to be independent of all + vertices. + + GIVEN an initialized dsu object + WHEN before executing dsu.merge(vertex a, vertex b) + THEN all the vertices are independent + ''' + + pair_of_vertices = self._generate_pair_of_vertices() + + for first_vertex, second_vertex in pair_of_vertices: + is_same = dsu.same(first_vertex, second_vertex) + assert not is_same + + for index in range(5): + assert dsu.size(index) == 1 + assert dsu.leader(index) == index + + assert dsu.groups() == [[0], [1], [2], [3], [4]] + + def _generate_pair_of_vertices(self): + from itertools import combinations + + return list(combinations(range(5), 2)) + def test_merge(self, dsu): ''' dsu.merge(vertex a, vertex b) is expected to be in the same group. @@ -26,6 +53,25 @@ def test_merge(self, dsu): is_same = dsu.same(0, 1) assert is_same + def test_merge_elements_of_the_same_group(self, dsu): + ''' + merge elements of the same group. + + GIVEN an initialized dsu object + WHEN merge vertex 0 and 1 twice + THEN vertex 0 and 1 are the same group. Their size is 2. + ''' + + is_same = dsu.same(0, 1) + assert not is_same + + for _ in range(2): + dsu.merge(0, 1) + is_same = dsu.same(0, 1) + assert is_same + assert dsu.size(0) == 2 + assert dsu.size(1) == 2 + def test_size(self, dsu): ''' dsu.size(vertex a) is expected to get size of vertex a. From 83592b63add2d7e9e490732c6d71e2554881ec9c Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Sun, 13 Sep 2020 15:11:50 +0900 Subject: [PATCH 6/9] Catch AssertionError if invalid input is given --- tests/test_dsu.py | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/tests/test_dsu.py b/tests/test_dsu.py index de4ecea..eea7231 100644 --- a/tests/test_dsu.py +++ b/tests/test_dsu.py @@ -117,3 +117,93 @@ def test_groups(self, dsu): dsu.merge(0, 2) assert dsu.groups() == [[0, 1, 2], [3], [4]] + + @pytest.mark.parametrize(( + 'vertex_a', 'vertex_b' + ), [ + (-2, 4), + (-1, 4), + (0, 5), + (0, 6), + (-1, 5), + (-1, 6), + ]) + def test_merge_failed_if_invalid_input_is_given(self, dsu, + vertex_a, vertex_b): + ''' + dsu.merge(vertex a, vertex b) is expected to be raised an + AssertionError if an invalid input is given. + + GIVEN an initialized dsu object + WHEN an out-of-range index is given + THEN raises an AssertionError + ''' + + with pytest.raises(AssertionError): + dsu.merge(vertex_a, vertex_b) + + @pytest.mark.parametrize(( + 'vertex_a', 'vertex_b' + ), [ + (-2, 4), + (-1, 4), + (0, 5), + (0, 6), + (-1, 5), + (-1, 6), + ]) + def test_same_failed_if_invalid_input_is_given(self, dsu, + vertex_a, vertex_b): + ''' + dsu.same(vertex a, vertex b) is expected to be raised an AssertionError + if an invalid input is given. + + GIVEN an initialized dsu object + WHEN an out-of-range index is given + THEN raises an AssertionError + ''' + + with pytest.raises(AssertionError): + dsu.same(vertex_a, vertex_b) + + @pytest.mark.parametrize(( + 'vertex_a' + ), [ + -2, + -1, + 5, + 6, + ]) + def test_leader_failed_if_invalid_input_is_given(self, dsu, vertex_a): + ''' + dsu.leader(vertex a) is expected to be raised an AssertionError if an + invalid input is given. + + GIVEN an initialized dsu object + WHEN an out-of-range index is given + THEN raises an AssertionError + ''' + + with pytest.raises(AssertionError): + dsu.leader(vertex_a) + + @pytest.mark.parametrize(( + 'vertex_a' + ), [ + -2, + -1, + 5, + 6, + ]) + def test_size_failed_if_invalid_input_is_given(self, dsu, vertex_a): + ''' + dsu.size(vertex a) is expected to be raised an AssertionError if an + invalid input is given. + + GIVEN an initialized dsu object + WHEN an out-of-range index is given + THEN raises an AssertionError + ''' + + with pytest.raises(AssertionError): + dsu.size(vertex_a) From 581d843d68ec5658e0c7697910802f599cb4fcce Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Sun, 13 Sep 2020 15:15:12 +0900 Subject: [PATCH 7/9] Remove conftest.py --- tests/conftest.py | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 tests/conftest.py diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index 8395053..0000000 --- a/tests/conftest.py +++ /dev/null @@ -1,8 +0,0 @@ -import os -import sys - -# Add path. -# See: -# https://www.magata.net/memo/index.php?pytest%C6%FE%CC%E7#y2046859 -path = os.path.dirname(os.path.abspath(__file__)) + '/../atcoder/' -sys.path.append(os.path.abspath(path)) From 7d251eb692e06d06d531985a798560a0b0f801c1 Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Sun, 13 Sep 2020 15:18:26 +0900 Subject: [PATCH 8/9] Rename --- tests/test_dsu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dsu.py b/tests/test_dsu.py index eea7231..4ab180c 100644 --- a/tests/test_dsu.py +++ b/tests/test_dsu.py @@ -53,7 +53,7 @@ def test_merge(self, dsu): is_same = dsu.same(0, 1) assert is_same - def test_merge_elements_of_the_same_group(self, dsu): + def test_merge_elements_of_same_group(self, dsu): ''' merge elements of the same group. From 238bc460319ca3a8310e127285c9f4ea9240cbed Mon Sep 17 00:00:00 2001 From: Kato Hiroki Date: Mon, 14 Sep 2020 06:31:39 +0900 Subject: [PATCH 9/9] Add test Reason: To make sure that leaders of two vertices are same --- tests/test_dsu.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_dsu.py b/tests/test_dsu.py index 4ab180c..9d68b82 100644 --- a/tests/test_dsu.py +++ b/tests/test_dsu.py @@ -100,6 +100,7 @@ def test_leader(self, dsu): assert dsu.leader(1) in [0, 1, 2] assert dsu.leader(2) in [0, 1, 2] + assert dsu.leader(1) == dsu.leader(2) assert dsu.leader(3) not in [0, 1, 2] assert dsu.leader(4) not in [0, 1, 2]