# pyRCV2: Preferential vote counting # Copyright © 2020–2021 Lee Yingtong Li (RunasSudo) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import pytest from pyRCV2.model import Candidate, CountCard from pyRCV2.numbers import Num from pyRCV2.ties import TiesBackwards, TiesForwards, TiesPrompt, TiesRandom import tests.util def test_prompt_py(monkeypatch): monkeypatch.setattr('builtins.input', lambda _: '2') l = [ (Candidate('A'), CountCard()), (Candidate('B'), CountCard()), (Candidate('C'), CountCard()), ] t = TiesPrompt() assert t.choose_lowest(l) == l[1] assert t.choose_highest(l) == l[1] def test_prompt_js(): ctx = tests.util.init_context() ctx.eval('let l = [[py.pyRCV2.model.Candidate("A"), py.pyRCV2.model.CountCard()], [py.pyRCV2.model.Candidate("B"), py.pyRCV2.model.CountCard()], [py.pyRCV2.model.Candidate("C"), py.pyRCV2.model.CountCard()]];') ctx.eval('let tie = py.pyRCV2.ties.TiesPrompt();') ctx.eval('let raised = false; try { tie.choose_lowest(l); } catch (ex) { if (py.isinstance(ex, py.pyRCV2.ties.RequireInput)) { raised = true; } }') assert ctx.eval('raised') == True ctx.eval('tie.buffer = "2";') assert ctx.eval('tie.choose_lowest(l) === l[1]') ctx.eval('tie.buffer = "2";') assert ctx.eval('tie.choose_highest(l) === l[1]') class Stub: pass def test_backwards_py(): l = [ (Candidate('A'), CountCard()), (Candidate('B'), CountCard()), (Candidate('C'), CountCard()), ] # Prepare previous results result1 = Stub() result1.candidates = {l[0][0]: CountCard(), l[1][0]: CountCard(), l[2][0]: CountCard()} result1.candidates[l[0][0]].orig_votes = Num(10) result1.candidates[l[1][0]].orig_votes = Num(20) result1.candidates[l[2][0]].orig_votes = Num(30) result2 = Stub() result2.candidates = {l[0][0]: CountCard(), l[1][0]: CountCard(), l[2][0]: CountCard()} result2.candidates[l[0][0]].orig_votes = Num(30) result2.candidates[l[1][0]].orig_votes = Num(20) result2.candidates[l[2][0]].orig_votes = Num(10) counter = Stub() counter.step_results = [result1, result2] t = TiesBackwards(counter) assert t.choose_lowest(l) == l[2] assert t.choose_highest(l) == l[0] def test_backwards_js(): ctx = tests.util.init_context() ctx.eval('let l = [[py.pyRCV2.model.Candidate("A"), py.pyRCV2.model.CountCard()], [py.pyRCV2.model.Candidate("B"), py.pyRCV2.model.CountCard()], [py.pyRCV2.model.Candidate("C"), py.pyRCV2.model.CountCard()]];') # Prepare previous results ctx.eval('let result1 = {candidates: py.pyRCV2.safedict.SafeDict([[l[0][0], {votes: py.pyRCV2.numbers.Num(10)}], [l[1][0], {votes: py.pyRCV2.numbers.Num(20)}], [l[2][0], {votes: py.pyRCV2.numbers.Num(30)}]])};') ctx.eval('let result2 = {candidates: py.pyRCV2.safedict.SafeDict([[l[0][0], {votes: py.pyRCV2.numbers.Num(30)}], [l[1][0], {votes: py.pyRCV2.numbers.Num(20)}], [l[2][0], {votes: py.pyRCV2.numbers.Num(10)}]])};') ctx.eval('let counter = {step_results: [result1, result2]};') ctx.eval('let tie = py.pyRCV2.ties.TiesBackwards(counter);') assert ctx.eval('tie.choose_lowest(l) === l[2]') assert ctx.eval('tie.choose_highest(l) === l[0]') def test_forwards_py(): l = [ (Candidate('A'), CountCard()), (Candidate('B'), CountCard()), (Candidate('C'), CountCard()), ] # Prepare previous results result1 = Stub() result1.candidates = {l[0][0]: CountCard(), l[1][0]: CountCard(), l[2][0]: CountCard()} result1.candidates[l[0][0]].orig_votes = Num(10) result1.candidates[l[1][0]].orig_votes = Num(20) result1.candidates[l[2][0]].orig_votes = Num(30) result2 = Stub() result2.candidates = {l[0][0]: CountCard(), l[1][0]: CountCard(), l[2][0]: CountCard()} result2.candidates[l[0][0]].orig_votes = Num(30) result2.candidates[l[1][0]].orig_votes = Num(20) result2.candidates[l[2][0]].orig_votes = Num(10) counter = Stub() counter.step_results = [result1, result2] t = TiesForwards(counter) assert t.choose_lowest(l) == l[0] assert t.choose_highest(l) == l[2] def test_forwards_js(): ctx = tests.util.init_context() ctx.eval('let l = [[py.pyRCV2.model.Candidate("A"), py.pyRCV2.model.CountCard()], [py.pyRCV2.model.Candidate("B"), py.pyRCV2.model.CountCard()], [py.pyRCV2.model.Candidate("C"), py.pyRCV2.model.CountCard()]];') # Prepare previous results ctx.eval('let result1 = {candidates: py.pyRCV2.safedict.SafeDict([[l[0][0], {votes: py.pyRCV2.numbers.Num(10)}], [l[1][0], {votes: py.pyRCV2.numbers.Num(20)}], [l[2][0], {votes: py.pyRCV2.numbers.Num(30)}]])};') ctx.eval('let result2 = {candidates: py.pyRCV2.safedict.SafeDict([[l[0][0], {votes: py.pyRCV2.numbers.Num(30)}], [l[1][0], {votes: py.pyRCV2.numbers.Num(20)}], [l[2][0], {votes: py.pyRCV2.numbers.Num(10)}]])};') ctx.eval('let counter = {step_results: [result1, result2]};') ctx.eval('let tie = py.pyRCV2.ties.TiesForwards(counter);') assert ctx.eval('tie.choose_lowest(l) === l[0]') assert ctx.eval('tie.choose_highest(l) === l[2]') def test_random_py(): l = [ (Candidate('A'), CountCard()), (Candidate('B'), CountCard()), (Candidate('C'), CountCard()), ] t = TiesRandom('foobar') # first number is 0, second number is 0 assert t.choose_lowest(l) == l[0] assert t.choose_highest(l) == l[0] def test_random_js(): ctx = tests.util.init_context() ctx.eval('let l = [[py.pyRCV2.model.Candidate("A"), py.pyRCV2.model.CountCard()], [py.pyRCV2.model.Candidate("B"), py.pyRCV2.model.CountCard()], [py.pyRCV2.model.Candidate("C"), py.pyRCV2.model.CountCard()]];') ctx.eval('let tie = py.pyRCV2.ties.TiesRandom("foobar")') assert ctx.eval('tie.choose_lowest(l) === l[0]') assert ctx.eval('tie.choose_highest(l) === l[0]')