Skip to content
8 changes: 8 additions & 0 deletions tests/io/data/abif/test001.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Case 1 - unordered scores
#
# Test case above copied from reddit post by /user/jman722
# https://www.reddit.com/r/EndFPTP/comments/nkm2cd/

12: Allie/5, Billy/5, Candace/4, Dennis/3, Edith/3, Frank/2, Georgie/1, Harold/0
7: Allie/4, Billy/0, Candace/2, Dennis/3, Edith/1, Frank/0, Georgie/5, Harold/3
5: Allie/0, Billy/3, Candace/2, Dennis/3, Edith/4, Frank/5, Georgie/3, Harold/4
5 changes: 5 additions & 0 deletions tests/io/data/abif/test002.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Case 2 - ranked ballots with ABCDEFGH candidate set

12: Allie=Billy>Candace>Dennis=Edith>Frank>Georgie>Harold
7: Georgie>Allie>Dennis=Harold>Candace>Edith>Billy=Frank
5: Frank>Edith=Harold>Billy=Dennis=Georgie>Candace>Allie
5 changes: 5 additions & 0 deletions tests/io/data/abif/test003.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Case 3 - Scores using = and > as delimiters

12: Allie/5 =Billy/5 >Candace/4 >Dennis/3 =Edith/3 >Frank/2 >Georgie/1 >Harold/0
7: Georgie/5 >Allie/4 >Dennis/3 =Harold/3 >Candace/2 >Edith/1 >Billy/0 =Frank/0
5: Frank/5 >Edith/4 =Harold/4 >Billy/3 =Dennis/3 =Georgie/3 >Candace/2 >Allie/0
7 changes: 7 additions & 0 deletions tests/io/data/abif/test004.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Case 4 - bracketed inlined tokens and unordered scores

27: [Doña García Márquez]/5, [Steven B. Jensen]/2, [Sue Ye (蘇業)]/1, [Adam Muñoz]/0
26: [Doña García Márquez]/3, [Steven B. Jensen]/5, [Sue Ye (蘇業)]/3, [Adam Muñoz]/1
24: [Doña García Márquez]/2, [Steven B. Jensen]/1, [Sue Ye (蘇業)]/5, [Adam Muñoz]/2
23: [Doña García Márquez]/1, [Steven B. Jensen]/0, [Sue Ye (蘇業)]/3, [Adam Muñoz]/5

12 changes: 12 additions & 0 deletions tests/io/data/abif/test005.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Case 5 - Declared, bracketed candidate tokens. Unordered scores.

[Doña García Márquez]: DGM
[Steven B. Jensen]: SBJ
[Sue Ye (蘇業)]: SY
[Adam Muñoz]: AM

27: DGM/5, SBJ/2, SY/1, AM/0
26: DGM/3, SBJ/5, SY/3, AM/1
24: DGM/2, SBJ/1, SY/5, AM/2
23: DGM/1, SBJ/0, SY/3, AM/5

11 changes: 11 additions & 0 deletions tests/io/data/abif/test006.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Case 6 - Bracketed candidate tokens (declared). Ranked and scored.

[Doña García Márquez]: DGM
[Steven B. Jensen]: SBJ
[Sue Ye (蘇業)]: SY
[Adam Muñoz]: AM

27: DGM/5 > SBJ/2 > SY/1 > AM/0
26: SBJ/5 > DGM/3 = SY/3 > AM/1
24: SY/5 > DGM/2 = AM/2 > SBJ/1
23: AM/5 > SY/3 > DGM/1 > SBJ/0
11 changes: 11 additions & 0 deletions tests/io/data/abif/test007.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Case 7 -Declared, bracketed candidate tokens. Ranked, no score.

[Doña García Márquez]: DGM
[Steven B. Jensen]: SBJ
[Sue Ye (蘇業)]: SY
[Adam Muñoz]: AM

27: DGM > SBJ > SY > AM
26: SBJ > DGM = SY > AM
24: SY > DGM = AM > SBJ
23: AM > SY > DGM > SBJ
10 changes: 10 additions & 0 deletions tests/io/data/abif/test008.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Case 8 - Mixed bracketed candidate tokens. Ranked, no score. Sans whitespace.

[Doña García Márquez]:DGM
[Steven B. Jensen]:SBJ
[Adam Muñoz]:AM

27:DGM>SBJ>[蘇業]>AM
26:SBJ>DGM=[蘇業]>AM
24:[蘇業]>DGM=AM>SBJ
23:AM>[蘇業]>DGM>SBJ
14 changes: 14 additions & 0 deletions tests/io/data/abif/test009.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Case 9 - Asterisk-delimited multiplier
#
# Suggested by Jan Šimbera in May 2020:
# http://lists.electorama.com/pipermail/election-methods-electorama.com/2021-June/002793.html

[Doña García Márquez]: DGM
[Steven B. Jensen]: SBJ
[Sue Ye (蘇業)]: SY
[Adam Muñoz]: AM

27 * DGM/5, SBJ/2, SY/1, AM/0
26 * DGM/3, SBJ/5, SY/3, AM/1
24 * DGM/2, SBJ/1, SY/5, AM/2
23 * DGM/1, SBJ/0, SY/3, AM/5
15 changes: 15 additions & 0 deletions tests/io/data/abif/test010.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Case 10 - Declared, bracketed candidate tokens. Unordered scores.
#
# 2021-06-18 - Replacement for case #5, using the new syntax described in
# ABIF issue #8 (<https://github.com/electorama/abif/issues/8>)

=DGM:[Doña García Márquez]
=SBJ:[Steven B. Jensen]
=SY:[Sue Ye (蘇業)]
=AM:[Adam Muñoz]

27: DGM/5, SBJ/2, SY/1, AM/0
26: DGM/3, SBJ/5, SY/3, AM/1
24: DGM/2, SBJ/1, SY/5, AM/2
23: DGM/1, SBJ/0, SY/3, AM/5

14 changes: 14 additions & 0 deletions tests/io/data/abif/test011.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Case 11 - Bracketed candidate tokens (declared). Ranked and scored.
#
# 2021-06-18 - Replacement for case #6, using the new syntax described in
# ABIF issue #8 (<https://github.com/electorama/abif/issues/8>)

=DGM:[Doña García Márquez]
=SBJ:[Steven B. Jensen]
=SY:[Sue Ye (蘇業)]
=AM:[Adam Muñoz]

27: DGM/5 > SBJ/2 > SY/1 > AM/0
26: SBJ/5 > DGM/3 = SY/3 > AM/1
24: SY/5 > DGM/2 = AM/2 > SBJ/1
23: AM/5 > SY/3 > DGM/1 > SBJ/0
14 changes: 14 additions & 0 deletions tests/io/data/abif/test012.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Case 12 - Declared, bracketed candidate tokens. Ranked, no score.
#
# 2021-06-18 - Replacement for case #7, using the new syntax described in
# ABIF issue #8 (<https://github.com/electorama/abif/issues/8>)

=DGM:[Doña García Márquez]
=SBJ:[Steven B. Jensen]
=SY:[Sue Ye (蘇業)]
=AM:[Adam Muñoz]

27: DGM > SBJ > SY > AM
26: SBJ > DGM = SY > AM
24: SY > DGM = AM > SBJ
23: AM > SY > DGM > SBJ
14 changes: 14 additions & 0 deletions tests/io/data/abif/test013.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Case 13 - Mixed bracketed candidate tokens. Ranked, no score. Sans whitespace.
#
# 2021-06-18 - Replacement for case #8, using the new syntax described in
# ABIF issue #8 (<https://github.com/electorama/abif/issues/8>)

=DGM:[Doña García Márquez]
=SBJ:[Steven B. Jensen]
=SY:[Sue Ye (蘇業)]
=AM:[Adam Muñoz]

27:DGM>SBJ>[蘇業]>AM
26:SBJ>DGM=[蘇業]>AM
24:[蘇業]>DGM=AM>SBJ
23:AM>[蘇業]>DGM>SBJ
16 changes: 16 additions & 0 deletions tests/io/data/abif/test014.abif
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Case 14 - Asterisk-delimited multiplier
#
# 2021-06-18 - Replacement for case #9, using the new syntax described in
# ABIF issue #8 (<https://github.com/electorama/abif/issues/8>)
# 2021-06-06 - Asterisk suggested by Jan Šimbera:
# <http://lists.electorama.com/pipermail/election-methods-electorama.com/2021-June/002793.html>

=DGM:[Doña García Márquez]
=SBJ:[Steven B. Jensen]
=SY:[Sue Ye (蘇業)]
=AM:[Adam Muñoz]

27 * DGM/5, SBJ/2, SY/1, AM/0
26 * DGM/3, SBJ/5, SY/3, AM/1
24 * DGM/2, SBJ/1, SY/5, AM/2
23 * DGM/1, SBJ/0, SY/3, AM/5
98 changes: 98 additions & 0 deletions tests/io/test_abif.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# coding: utf8

import sys
import os
import io
import logging

import pytest

sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
import votelib.io.abif


DATA_DIR = os.path.join(os.path.dirname(__file__), 'data', 'abif')

eqr = lambda *args: frozenset(args)

def nonascii_result(ye_name):
return {
('Doña García Márquez', 'Steven B. Jensen', ye_name, 'Adam Muñoz'): 27,
('Steven B. Jensen', eqr('Doña García Márquez', ye_name), 'Adam Muñoz'): 26,
(ye_name, eqr('Doña García Márquez', 'Adam Muñoz'), 'Steven B. Jensen'): 24,
('Adam Muñoz', ye_name, 'Doña García Márquez', 'Steven B. Jensen'): 23,
}

FILES_EXPECTED = {
'test001': {
frozenset([('Allie', 5), ('Billy', 5), ('Candace', 4), ('Dennis', 3), ('Edith', 3), ('Frank', 2), ('Georgie', 1), ('Harold', 0)]): 12,
frozenset([('Allie', 4), ('Billy', 0), ('Candace', 2), ('Dennis', 3), ('Edith', 1), ('Frank', 0), ('Georgie', 5), ('Harold', 3)]): 7,
frozenset([('Allie', 0), ('Billy', 3), ('Candace', 2), ('Dennis', 3), ('Edith', 4), ('Frank', 5), ('Georgie', 3), ('Harold', 4)]): 5,
},
'test002': {
(eqr('Allie', 'Billy'), 'Candace', eqr('Dennis', 'Edith'), 'Frank', 'Georgie', 'Harold'): 12,
('Georgie', 'Allie', eqr('Dennis', 'Harold'), 'Candace', 'Edith', eqr('Billy', 'Frank')): 7,
('Frank', eqr('Edith', 'Harold'), eqr('Billy', 'Dennis', 'Georgie'), 'Candace', 'Allie'): 5,
},
'test004': {
frozenset([('Doña García Márquez', 5), ('Steven B. Jensen', 2), ('Sue Ye (蘇業)', 1), ('Adam Muñoz', 0)]): 27,
frozenset([('Doña García Márquez', 3), ('Steven B. Jensen', 5), ('Sue Ye (蘇業)', 3), ('Adam Muñoz', 1)]): 26,
frozenset([('Doña García Márquez', 2), ('Steven B. Jensen', 1), ('Sue Ye (蘇業)', 5), ('Adam Muñoz', 2)]): 24,
frozenset([('Doña García Márquez', 1), ('Steven B. Jensen', 0), ('Sue Ye (蘇業)', 3), ('Adam Muñoz', 5)]): 23,
},
'test012': nonascii_result('Sue Ye (蘇業)'),
'test013': nonascii_result('蘇業'),
}
FILES_EXPECTED['test003'] = FILES_EXPECTED['test001']
FILES_EXPECTED['test010'] = FILES_EXPECTED['test004']
FILES_EXPECTED['test011'] = FILES_EXPECTED['test004']
FILES_EXPECTED['test014'] = FILES_EXPECTED['test004']

FAILS = [
': A>B', # missing ballot count number
'0xab: A>B', # invalid ballot count number
'A>B \nA>B \nB>A', # no ballot counts
'some random text \n3: A>B=C>D', # free hanging text
'3: >B>C', # does not start with candidate token
'3: A>>C', # missing candidate token
'3: A>=C', # missing candidate token
'3: A$$>C', # invalid candidate token
'2: A > C, B, D', # mixing commas and rankings in non-score vote
'2: A, B > C = D', # mixing commas and rankings in non-score vote
'1: A/2, B, C, D', # mixing score vote and other
'1: A/2, B', # mixing score vote and other
'1: A, B/3, C/2, D', # mixing score vote and other
'1: A/1 B/3 C/2', # score vote w/o separator
'1: A B C', # approval vote w/o separator
'1: Á > Č = Ď', # unbracketed non-ascii
'1: [A > B', # unclosed bracket
# TODO add candidate mapping parsing errors after that part of spec is stabilized
]

FILE_FAILS = ['test005', 'test006', 'test007', 'test008', 'test009']


@pytest.mark.parametrize('filename, expected', list(FILES_EXPECTED.items()))
def test_files_output(filename, expected):
with open(os.path.join(DATA_DIR, f'{filename}.abif'), 'r', encoding='utf8') as infile:
result = votelib.io.abif.load(infile)
assert result == expected


@pytest.mark.parametrize('expected', list(FILES_EXPECTED.values()))
def test_roundtrip(expected):
roundtripped = votelib.io.abif.loads(votelib.io.abif.dumps(expected))
assert roundtripped == expected


@pytest.mark.parametrize('abif', FAILS)
def test_fail(abif):
with pytest.raises(votelib.io.abif.ABIFParseError):
result = votelib.io.abif.loads(abif)


@pytest.mark.parametrize('filename', FILE_FAILS)
def test_file_fail(filename):
with pytest.raises(votelib.io.abif.ABIFParseError):
with open(os.path.join(DATA_DIR, f'{filename}.abif'), 'r', encoding='utf8') as infile:
result = votelib.io.abif.load(infile)
Loading