Beispiel #1
0
    def test_all_vacancies_filled_should_be_true_after_round_runs(self):
        """
        Before a round has run it should report that all vacancies have not
        been filled. After the round runs it should report that all vacancies
        have been filled.
        """

        vacancies = 2
        candidates = ('Red', 'Green', 'Blue')
        votes = 4 * (('Red', ), ) + \
                3 * (('Green', ), )

        stv_round = Round(vacancies, candidates, votes)
        self.assertFalse(stv_round.all_vacancies_filled())

        stv_round.run()
        self.assertTrue(stv_round.all_vacancies_filled())
Beispiel #2
0
    def test_all_vacancies_filled_should_be_false_after_incomplete_round(self):
        """
        After a round runs that fails to elect enough candidates the round
        should report that it has not filled all vacancies.
        """

        vacancies = 2
        candidates = ('Red', 'Green', 'Blue', 'Yellow', 'Mauve')
        votes = 5 * (('Red', ), ) + \
                4 * (('Green', ), ) + \
                3 * (('Blue', ), ) + \
                2 * (('Yellow', ), ) + \
                1 * (('Mauve', ), )

        stv_round = Round(vacancies, candidates, votes)
        stv_round.run()
        self.assertFalse(stv_round.all_vacancies_filled())
Beispiel #3
0
    def test_candidates_should_be_elected_once_there_is_one_per_vacanc(self):
        """
        As soon as there are the same number of remaining candidates as
        vacancies the election is completed with all of the remaining
        candidates elected as winners.

        Note that this makes it possible for a candidate to be elected even
        without enough votes to reach the quota, as in this example.

        Surplus votes for candidates A and B become exhausted votes because
        there is no further preference to reallocate them to. Candidate D is
        then excluded because they have the fewest votes. Since this leaves
        only three candidates for three vacancies candidate C is declared
        elected, even though they have only one vote compared to the quota of
        three.
        """

        vacancies = 3
        candidates = ('A', 'B', 'C', 'D')
        votes = 5 * (('A', 'B'), ) + \
                4 * (('B', 'A'), ) + \
                1 * (('C', ), )

        expected_results = {
            'provisionally_elected': {
                'A': 3,
                'B': 3,
                'C': 1
            },
            'continuing': {},
            'excluded': {
                'D': 0
            },
        }

        stv = Round(vacancies, candidates, votes)
        stv.run()
        self.assertEqual(expected_results, stv.results())
        self.assertTrue(stv.all_vacancies_filled())
Beispiel #4
0
    def test_reallocate_candidate_reaching_quota(self):
        """
        This test steps through the run round algorithm, demonstrating
        each step.

        Only one candidate reaches the quota initially, but reallocation
        causes another to reach quota and require reallocation of their now
        surplus votes.

        Note that this reallocation of Mars's now surplus votes requires their
        devaluing to have been recorded, i.e. it's not 8 at the end of the
        first iteration, it's 11 votes worth 8/11ths each.
        """
        votes = [
            ['Galaxy', 'Mars', 'Crunchie'],
            ['Galaxy', 'Mars', 'Crunchie'],
            ['Galaxy', 'Mars', 'Crunchie'],
            ['Galaxy', 'Mars', 'Crunchie'],
            ['Galaxy', 'Mars', 'Crunchie'],
            ['Galaxy', 'Mars', 'Crunchie'],
            ['Galaxy', 'Mars', 'Crunchie'],
            ['Galaxy', 'Mars', 'Crunchie'],
            ['Galaxy', 'Mars', 'Crunchie'],
            ['Galaxy', 'Mars', 'Crunchie'],
            ['Galaxy', 'Mars', 'Bounty'],
        ]
        vacancies = 3
        candidates = ['Mars', 'Bounty', 'Galaxy', 'Crunchie']

        # At this stage, we have calculated initial totals but
        # done no more work with the votes
        stv_round = Round(vacancies, candidates, votes)

        initial_totals = {
            'provisionally_elected': {},
            'continuing': {
                'Mars': 0,
                'Bounty': 0,
                'Galaxy': 11,
                'Crunchie': 0,
            },
            'excluded': {}
        }

        self.assertEqual(initial_totals, stv_round.results())

        # Now we run the first pass of calculating which
        # candidates are provisionally elected
        stv_round._provisionally_elect_candidates()

        first_provisional_election_totals = {
            'provisionally_elected': {
                'Galaxy': 11,
            },
            'continuing': {
                'Mars': 0,
                'Bounty': 0,
                'Crunchie': 0,
            },
            'excluded': {}
        }

        self.assertEqual(first_provisional_election_totals, stv_round.results())

        # Now we reassign Galaxy's surplus votes to the
        # next preferences of votes for Galaxy.
        # Note that the 8 for Mars is actually 11 votes each worth 8/11ths.
        stv_round._reassign_votes_from_candidate_with_highest_surplus()

        first_reallocation_totals = {
            'provisionally_elected': {
                'Galaxy': 3,
            },
            'continuing': {
                'Mars': 8,
                'Bounty': 0,
                'Crunchie': 0,
            },
            'excluded': {}
        }

        self.assertEqual(first_reallocation_totals, stv_round.results())

        # After reallocation, we run a provisional election again to see if any
        # new candidates have reached the quota
        stv_round._provisionally_elect_candidates()

        second_provisional_election_totals = {
            'provisionally_elected': {
                'Galaxy': 3,
                'Mars': 8,
            },
            'continuing': {
                'Bounty': 0,
                'Crunchie': 0,
            },
            'excluded': {}
        }

        self.assertEqual(second_provisional_election_totals, stv_round.results())

        # Now Mars has reached the quota, we need to reallocate
        # Mars's surplus votes to the next preferences of votes
        # for Mars.
        # However, votes that have been allocated to Mars from Galaxy
        # are not worth 1, they are worth 8/11 because of their
        # previous reallocation from Galaxy.
        stv_round._reassign_votes_from_candidate_with_highest_surplus()

        second_reallocation_totals = {
            'provisionally_elected': {
                'Galaxy': 3,
                'Mars': 3,
            },
            'continuing': {
                'Bounty': Fraction(5,11),
                'Crunchie': 4 + Fraction(6, 11)
            },
            'excluded': {}
        }

        self.assertEqual(second_reallocation_totals, stv_round.results())

        # We now provisonally elect candidates again to see if anyone else has
        # reached the quota - Crunchie has
        stv_round._provisionally_elect_candidates()

        third_provisional_election_total = {
            'provisionally_elected': {
                'Galaxy': 3,
                'Mars': 3,
                'Crunchie': 4 + Fraction(6, 11)
            },
            'continuing': {
                'Bounty': Fraction(5,11),
            },
            'excluded': {}
        }

        self.assertEqual(third_provisional_election_total, stv_round.results())

        # Now that we have three provisionally elected candidates for the three
        # vacancies, the election is over
        self.assertTrue(stv_round.all_vacancies_filled())