From 0d7b1c3bd14d3bc0cfe472c3851039bde606a40a Mon Sep 17 00:00:00 2001 From: crflynn Date: Mon, 8 Jan 2018 22:12:31 -0500 Subject: [PATCH] tests --- Pipfile | 2 + Pipfile.lock | 87 +++++++++++++++++++++++++++++++++++-- pytest.ini | 2 + tests/__init__.py | 0 tests/conftest.py | 87 +++++++++++++++++++++++++++++++++++++ tests/test_apportionment.py | 11 +++++ tests/test_diversity.py | 18 ++++++++ tests/test_proportion.py | 16 +++++++ tests/test_quota.py | 7 +++ voting/apportionment.py | 2 +- voting/diversity.py | 9 +++- 11 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 pytest.ini create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_apportionment.py create mode 100644 tests/test_diversity.py create mode 100644 tests/test_proportion.py create mode 100644 tests/test_quota.py diff --git a/Pipfile b/Pipfile index 75d227f..9b36315 100644 --- a/Pipfile +++ b/Pipfile @@ -15,3 +15,5 @@ sphinx-rtd-theme = "*" [dev-packages] ptpython = "*" +pytest = "*" +pytest-cov = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 118c8ae..780ba59 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "9ac7ae345861a62cb069f25af526478a0d6ad267299653f8ff51a35e4afc0abb" + "sha256": "c77bc9703a73f19a9d5b39563ef31fe3a6af909adc98ee2c316ae46f02d34f5e" }, "host-environment-markers": { "implementation_name": "cpython", @@ -179,10 +179,10 @@ }, "sphinx": { "hashes": [ - "sha256:fdf77f4f30d84a314c797d67fe7d1b46665e6c48a25699d7bf0610e05a2221d4", - "sha256:c6de5dbdbb7a0d7d2757f4389cc00e8f6eb3c49e1772378967a12cfcf2cfe098" + "sha256:b8baed19394af85b21755c68c7ec4eac57e8a482ed89cd01cd5d5ff72200fe0f", + "sha256:c39a6fa41bd3ec6fc10064329a664ed3a3ca2e27640a823dc520c682e4433cdb" ], - "version": "==1.6.5" + "version": "==1.6.6" }, "sphinx-autobuild": { "hashes": [ @@ -230,6 +230,58 @@ } }, "develop": { + "attrs": { + "hashes": [ + "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450", + "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9" + ], + "version": "==17.4.0" + }, + "coverage": { + "hashes": [ + "sha256:d1ee76f560c3c3e8faada866a07a32485445e16ed2206ac8378bd90dadffb9f0", + "sha256:007eeef7e23f9473622f7d94a3e029a45d55a92a1f083f0f3512f5ab9a669b05", + "sha256:17307429935f96c986a1b1674f78079528833410750321d22b5fb35d1883828e", + "sha256:845fddf89dca1e94abe168760a38271abfc2e31863fbb4ada7f9a99337d7c3dc", + "sha256:3f4d0b3403d3e110d2588c275540649b1841725f5a11a7162620224155d00ba2", + "sha256:4c4f368ffe1c2e7602359c2c50233269f3abe1c48ca6b288dcd0fb1d1c679733", + "sha256:f8c55dd0f56d3d618dfacf129e010cbe5d5f94b6951c1b2f13ab1a2f79c284da", + "sha256:cdd92dd9471e624cd1d8c1a2703d25f114b59b736b0f1f659a98414e535ffb3d", + "sha256:2ad357d12971e77360034c1596011a03f50c0f9e1ecd12e081342b8d1aee2236", + "sha256:e9a0e1caed2a52f15c96507ab78a48f346c05681a49c5b003172f8073da6aa6b", + "sha256:eea9135432428d3ca7ee9be86af27cb8e56243f73764a9b6c3e0bda1394916be", + "sha256:700d7579995044dc724847560b78ac786f0ca292867447afda7727a6fbaa082e", + "sha256:66f393e10dd866be267deb3feca39babba08ae13763e0fc7a1063cbe1f8e49f6", + "sha256:5ff16548492e8a12e65ff3d55857ccd818584ed587a6c2898a9ebbe09a880674", + "sha256:d00e29b78ff610d300b2c37049a41234d48ea4f2d2581759ebcf67caaf731c31", + "sha256:87d942863fe74b1c3be83a045996addf1639218c2cb89c5da18c06c0fe3917ea", + "sha256:358d635b1fc22a425444d52f26287ae5aea9e96e254ff3c59c407426f44574f4", + "sha256:81912cfe276e0069dca99e1e4e6be7b06b5fc8342641c6b472cb2fed7de7ae18", + "sha256:079248312838c4c8f3494934ab7382a42d42d5f365f0cf7516f938dbb3f53f3f", + "sha256:b0059630ca5c6b297690a6bf57bf2fdac1395c24b7935fd73ee64190276b743b", + "sha256:493082f104b5ca920e97a485913de254cbe351900deed72d4264571c73464cd0", + "sha256:e3ba9b14607c23623cf38f90b23f5bed4a3be87cbfa96e2e9f4eabb975d1e98b", + "sha256:82cbd3317320aa63c65555aa4894bf33a13fb3a77f079059eb5935eea415938d", + "sha256:9721f1b7275d3112dc7ccf63f0553c769f09b5c25a26ee45872c7f5c09edf6c1", + "sha256:bd4800e32b4c8d99c3a2c943f1ac430cbf80658d884123d19639bcde90dad44a", + "sha256:f29841e865590af72c4b90d7b5b8e93fd560f5dea436c1d5ee8053788f9285de", + "sha256:f3a5c6d054c531536a83521c00e5d4004f1e126e2e2556ce399bef4180fbe540", + "sha256:dd707a21332615108b736ef0b8513d3edaf12d2a7d5fc26cd04a169a8ae9b526", + "sha256:2e1a5c6adebb93c3b175103c2f855eda957283c10cf937d791d81bef8872d6ca", + "sha256:f87f522bde5540d8a4b11df80058281ac38c44b13ce29ced1e294963dd51a8f8", + "sha256:a7cfaebd8f24c2b537fa6a271229b051cdac9c1734bb6f939ccfc7c055689baa", + "sha256:309d91bd7a35063ec7a0e4d75645488bfab3f0b66373e7722f23da7f5b0f34cc", + "sha256:0388c12539372bb92d6dde68b4627f0300d948965bbb7fc104924d715fdc0965", + "sha256:ab3508df9a92c1d3362343d235420d08e2662969b83134f8a97dc1451cbe5e84", + "sha256:43a155eb76025c61fc20c3d03b89ca28efa6f5be572ab6110b2fb68eda96bfea", + "sha256:f98b461cb59f117887aa634a66022c0bd394278245ed51189f63a036516e32de", + "sha256:b6cebae1502ce5b87d7c6f532fa90ab345cfbda62b95aeea4e431e164d498a3d", + "sha256:a4497faa4f1c0fc365ba05eaecfb6b5d24e3c8c72e95938f9524e29dadb15e76", + "sha256:2b4d7f03a8a6632598cbc5df15bbca9f778c43db7cf1a838f4fa2c8599a8691a", + "sha256:1afccd7e27cac1b9617be8c769f6d8a6d363699c9b86820f40c74cfb3328921c" + ], + "version": "==4.4.2" + }, "docopt": { "hashes": [ "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491" @@ -250,6 +302,12 @@ ], "version": "==0.1.1" }, + "pluggy": { + "hashes": [ + "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff" + ], + "version": "==0.6.0" + }, "prompt-toolkit": { "hashes": [ "sha256:3f473ae040ddaa52b52f97f6b4a493cfa9f5920c255a12dc56a7d34397a398a4", @@ -266,6 +324,13 @@ ], "version": "==0.41" }, + "py": { + "hashes": [ + "sha256:8cca5c229d225f8c1e3085be4fcf306090b00850fefad892f9d96c7b6e2f310f", + "sha256:ca18943e28235417756316bfada6cd96b23ce60dd532642690dcfdaba988a76d" + ], + "version": "==1.5.2" + }, "pygments": { "hashes": [ "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", @@ -273,6 +338,20 @@ ], "version": "==2.2.0" }, + "pytest": { + "hashes": [ + "sha256:b84878865558194630c6147f44bdaef27222a9f153bbd4a08908b16bf285e0b1", + "sha256:53548280ede7818f4dc2ad96608b9f08ae2cc2ca3874f2ceb6f97e3583f25bc4" + ], + "version": "==3.3.2" + }, + "pytest-cov": { + "hashes": [ + "sha256:890fe5565400902b0c78b5357004aab1c814115894f4f21370e2433256a3eeec", + "sha256:03aa752cf11db41d281ea1d807d954c4eda35cfa1b21d6971966cc041bbf6e2d" + ], + "version": "==2.5.1" + }, "six": { "hashes": [ "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb", diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..3c92031 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = -rsx --cov=voting tests/ --cov-report=html:_cov 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..93bef18 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,87 @@ +"""Fixtures.""" +# flake8: noqa +import pytest + +from voting.apportionment import adams +from voting.apportionment import dhondt +from voting.apportionment import hagenbach_bischoff +from voting.apportionment import hamilton +from voting.apportionment import huntington_hill +from voting.apportionment import jefferson +from voting.apportionment import sainte_lague +from voting.apportionment import vinton +from voting.apportionment import webster +from voting.diversity import berger_parker +from voting.diversity import general +from voting.diversity import gini_simpson +from voting.diversity import golosov +from voting.diversity import inverse_simpson +from voting.diversity import laakso_taagepera +from voting.diversity import renyi +from voting.diversity import shannon +from voting.diversity import simpson +from voting.proportion import adjusted_loosemore_hanby +from voting.proportion import dhondt as dh # dupe of apportionment +from voting.proportion import gallagher +from voting.proportion import grofman +from voting.proportion import least_square +from voting.proportion import lijphart +from voting.proportion import loosemore_hanby +from voting.proportion import rae +from voting.proportion import regression +from voting.proportion import rose +from voting.proportion import sainte_lague as sl # dupe of apportionment +from voting.quota import droop +from voting.quota import hagenbach_bischoff as hb # dupe of apportionment +from voting.quota import hare +from voting.quota import imperiali + +@pytest.fixture(params=[[2560, 3315, 995, 5012]]) +def votes(request): + return request.param + +@pytest.fixture(params=[20]) +def seats(request): + return request.param + +@pytest.fixture(params=list(range(4, 50))) +def seats_val(request): + return request.param + +@pytest.fixture(params=[[5, 6, 7, 8]]) +def seats_list(request): + return request.param + +@pytest.fixture(params=[adams, dhondt, hagenbach_bischoff, hamilton, + huntington_hill, jefferson, sainte_lague, vinton, webster +]) +def apportionment_method(request): + return request.param + +@pytest.fixture(params=[droop, hb, hare, imperiali]) +def quota_method(request): + return request.param + +@pytest.fixture(params=[droop, hb, hare, imperiali]) +def quota_method(request): + return request.param + +@pytest.fixture(params=[adjusted_loosemore_hanby, dh, gallagher, grofman, + least_square, lijphart, loosemore_hanby, rae, regression, rose, sl +]) +def proportion_method(request): + return request.param + +@pytest.fixture(params=['seats', 'votes', 'something_else']) +def parties(request): + return request.param + +@pytest.fixture(params=[berger_parker, general, gini_simpson, golosov, + inverse_simpson, laakso_taagepera, renyi, shannon, simpson +]) +def diversity_method(request): + return request.param + +@pytest.fixture(params=[0, 0.5, 1, 2]) +def q(request): + return request.param diff --git a/tests/test_apportionment.py b/tests/test_apportionment.py new file mode 100644 index 0000000..b893467 --- /dev/null +++ b/tests/test_apportionment.py @@ -0,0 +1,11 @@ +"""Apportionment method tests.""" +# flake8: noqa +import pytest + + +def test_apportionment_sum(apportionment_method, votes, seats): + assert sum(apportionment_method(votes, seats)) == seats + + +def test_apportionment_values(apportionment_method, votes, seats_val): + assert sum(apportionment_method(votes, seats_val)) == seats_val diff --git a/tests/test_diversity.py b/tests/test_diversity.py new file mode 100644 index 0000000..39987ca --- /dev/null +++ b/tests/test_diversity.py @@ -0,0 +1,18 @@ +"""Diversity tests.""" +# flake8: noqa +import pytest + +from voting.diversity import general +from voting.diversity import renyi + + +def test_diversity_method(diversity_method, votes): + assert isinstance(diversity_method(votes), float) + + +def test_general_q(votes, q): + assert isinstance(general(votes, q), float) + + +def test_renyi_q(votes, q): + assert isinstance(renyi(votes, q), float) diff --git a/tests/test_proportion.py b/tests/test_proportion.py new file mode 100644 index 0000000..399c523 --- /dev/null +++ b/tests/test_proportion.py @@ -0,0 +1,16 @@ +"""Proportion tests.""" +# flake8: noqa +import pytest + +from voting.proportion import grofman + + +def test_proportion_method(proportion_method, votes, seats_list): + assert proportion_method(votes, seats_list) > 0 + +def test_grofman_parties(votes, seats_list, parties): + if parties not in ('votes', 'seats'): + with pytest.raises(ValueError): + grofman(votes, seats_list, parties) + else: + assert grofman(votes, seats_list, parties) > 0 diff --git a/tests/test_quota.py b/tests/test_quota.py new file mode 100644 index 0000000..8c6f97a --- /dev/null +++ b/tests/test_quota.py @@ -0,0 +1,7 @@ +"""Quota tests.""" +# flake8: noqa +import pytest + + +def test_quota_method(quota_method, seats): + assert quota_method(100, seats) > 0 diff --git a/voting/apportionment.py b/voting/apportionment.py index c358e56..f93b58d 100644 --- a/voting/apportionment.py +++ b/voting/apportionment.py @@ -18,7 +18,7 @@ def adams(votes, seats): surplus = int(sum(upper) - seats) if surplus > 0: # divisor diffs that would remove another seat to each group - diffs = [1.0 * vote / floor(i + dec) + diffs = [1.0 * vote / max(floor(i + dec), 1) for i, dec, vote in zip(lower, decs, votes)] # argsort low to high divs = [i[0] for i in sorted(enumerate(diffs), key=itemgetter(1))] diff --git a/voting/diversity.py b/voting/diversity.py index cfe93fc..2245308 100644 --- a/voting/diversity.py +++ b/voting/diversity.py @@ -1,4 +1,5 @@ """Measures of diversity.""" +from math import exp from math import log from voting.util import normalize @@ -17,7 +18,7 @@ def berger_parker(groups): return max(groups) -def general(groups, q): +def general(groups, q=1): r"""Calculate the general diversity index. .. math:: @@ -27,6 +28,8 @@ def general(groups, q): :param list groups: a list of integers representing populations of groups :param float q: weight value """ + if q == 1: + return exp(shannon(groups)) groups = normalize(groups) return sum([g ** q for g in groups]) ** (1.0 / (1 - q)) @@ -84,7 +87,7 @@ def laakso_taagepera(groups): return 1.0 / sum([g ** 2 for g in groups]) -def renyi(groups, q=1): +def renyi(groups, q=0): r"""Calculate the Renyi entropy. .. math:: @@ -95,6 +98,8 @@ def renyi(groups, q=1): :param float q: weight value """ + if q == 1: + return shannon(groups) groups = normalize(groups) return 1.0 / (1 - q) * log(sum([g ** q for g in groups]))