1
0
This repository has been archived on 2023-11-30. You can view files and clone it, but cannot push or open issues or pull requests.
dtux__serveur-vote-lalis/include/condorcet/Tests/src/ElectionTest.php

986 lines
32 KiB
PHP
Raw Normal View History

2022-09-21 12:39:11 +02:00
<?php
declare(strict_types=1);
namespace CondorcetPHP\Condorcet\Tests;
use CondorcetPHP\Condorcet\ElectionProcess\ElectionState;
use CondorcetPHP\Condorcet\{Candidate, Condorcet, Election, Vote};
use CondorcetPHP\Condorcet\Throwable\{CandidateDoesNotExistException, CandidateExistsException, ElectionObjectVersionMismatchException, FileDoesNotExistException, NoCandidatesException, NoSeatsException, ResultRequestedWithoutVotesException, VoteException, VoteInvalidFormatException, VoteMaxNumberReachedException, VotingHasStartedException};
use CondorcetPHP\Condorcet\Tools\Converters\CondorcetElectionFormat;
use PHPUnit\Framework\TestCase;
class ElectionTest extends TestCase
{
private Election $election1;
private Election $election2;
private Candidate $candidate1;
private Candidate $candidate2;
private Candidate $candidate3;
private Vote $vote1;
private Vote $vote2;
private Vote $vote3;
private Vote $vote4;
protected function setUp(): void
{
$this->election1 = new Election;
$this->candidate1 = $this->election1->addCandidate('candidate1');
$this->candidate2 = $this->election1->addCandidate('candidate2');
$this->candidate3 = $this->election1->addCandidate('candidate3');
$this->election1->addVote($this->vote1 = new Vote([$this->candidate1, $this->candidate2, $this->candidate3]));
$this->election1->addVote($this->vote2 = new Vote([$this->candidate2, $this->candidate3, $this->candidate1]));
$this->election1->addVote($this->vote3 = new Vote([$this->candidate3, $this->candidate1, $this->candidate2]));
$this->election1->addVote($this->vote4 = new Vote([$this->candidate1, $this->candidate2, $this->candidate3]));
$this->election2 = new Election;
}
public function testRemoveVotes(): never
{
$this->expectException(VoteException::class);
$this->expectExceptionMessage('Problem handling vote: cannot remove vote, is not registered in this election');
self::assertTrue($this->election1->removeVote($this->vote2));
self::assertCount(3, $this->election1->getVotesList());
$badRemoveVote = new Vote('A');
$this->election1->removeVote($badRemoveVote);
}
public function testRemoveVotesByTags(): void
{
$this->vote1->addtags('tag1,tag2,tag3');
$this->vote2->addtags('tag3,tag4');
$this->vote3->addtags('tag3,tag4,tag5');
$this->vote4->addtags('tag1,tag4');
self::assertCount(3, $r = $this->election1->removeVotesByTags(['tag1', 'tag5']));
self::assertSame([$this->vote1, $this->vote3, $this->vote4], $r);
self::assertSame([1 => $this->vote2], $this->election1->getVotesList());
$this->setUp();
$this->vote1->addtags('tag1,tag2,tag3');
$this->vote2->addtags('tag3,tag4');
$this->vote3->addtags('tag3,tag4,tag5');
$this->vote4->addtags('tag1,tag4');
self::assertCount(1, $r = $this->election1->removeVotesByTags('tag1,tag5', false));
self::assertSame([$this->vote2], $r);
self::assertSame([0 => $this->vote1, 2=> $this->vote3, 3 => $this->vote4], $this->election1->getVotesList());
}
public function testTagsFilter(): void
{
$this->vote1->addtags('tag1,tag2,tag3');
$this->vote2->addtags('tag3,tag4');
$this->vote3->addtags('tag3,tag4,tag5');
$this->vote4->addtags('tag1,tag4');
self::assertSame($this->election1->getVotesList('tag1,tag2', true), [0=>$this->vote1, 3=>$this->vote4]);
self::assertSame($this->election1->countVotes('tag1,tag2', true), 2);
self::assertSame($this->election1->getVotesList('tag1,tag2', false), [1=>$this->vote2, 2=>$this->vote3]);
self::assertSame($this->election1->countVotes('tag1,tag2', false), 2);
$resultGlobal = $this->election1->getResult('Schulze');
$resultFilter1 = $this->election1->getResult('Schulze', ['tags' => 'tag1', 'withTag' => true]);
$resultFilter2 = $this->election1->getResult('Schulze', ['tags' => 'tag1', 'withTag' => false]);
self::assertNotSame($resultGlobal, $resultFilter1);
self::assertNotSame($resultGlobal, $resultFilter2);
self::assertNotSame($resultFilter1, $resultFilter2);
}
public function testParseCandidates(): void
{
self::assertCount(
4,
$this->election2->parseCandidates('Bruckner; Mahler ;
Debussy
Bibendum')
);
self::assertSame(
['Bruckner', 'Mahler', 'Debussy', 'Bibendum'],
$this->election2->getCandidatesListAsString()
);
}
public function testgetCandidateObjectFromName(): void
{
self::assertSame($this->candidate1, $this->election1->getCandidateObjectFromName('candidate1'));
self::assertNull($this->election1->getCandidateObjectFromName('candidate42'));
}
public function testParseError(): never
{
$this->expectException(VoteInvalidFormatException::class);
$this->expectExceptionMessage("The format of the vote is invalid: the value 'text' is not an integer.");
$this->election1->parseVotes('candidate1>candidate2 * text');
}
/**
* @preserveGlobalState disabled
* @backupStaticAttributes disabled
* @runInSeparateProcess
*/
public function testMaxParseIteration1(): never
{
$this->expectException(VoteMaxNumberReachedException::class);
$this->expectExceptionMessage('The maximal number of votes for the method is reached: 42');
self::assertSame(42, Election::setMaxParseIteration(42));
self::assertCount(42, $this->election1->parseVotes('candidate1>candidate2 * 42'));
self::assertCount(42, $this->election1->parseVotes('candidate1>candidate2 * 42'));
self::assertNull(Election::setMaxParseIteration(null));
self::assertCount(43, $this->election1->parseVotes('candidate1>candidate2 * 43'));
self::assertSame(42, Election::setMaxParseIteration(42));
$this->election1->parseVotes('candidate1>candidate2 * 43');
}
/**
* @preserveGlobalState disabled
* @backupStaticAttributes disabled
* @runInSeparateProcess
*/
public function testMaxParseIteration2(): never
{
$this->expectException(VoteMaxNumberReachedException::class);
$this->expectExceptionMessage('The maximal number of votes for the method is reached: 42');
self::assertSame(42, Election::setMaxParseIteration(42));
self::assertSame(42, $this->election1->parseVotes('
candidate1>candidate2 * 21
candidate1>candidate2 * 21
candidate1>candidate2 * 21
'));
}
/**
* @preserveGlobalState disabled
* @backupStaticAttributes disabled
* @runInSeparateProcess
*/
public function testMaxParseIteration3(): never
{
$this->expectException(VoteMaxNumberReachedException::class);
$this->expectExceptionMessage('The maximal number of votes for the method is reached: 2');
self::assertSame(2, Election::setMaxParseIteration(2));
self::assertSame([0=>'candidate1', 1=>'candidate2'], $this->election2->parseCandidates('candidate1;candidate2'));
self::assertSame([0=>'candidate3', 1=>'candidate4'], $this->election2->parseCandidates('candidate3;candidate4'));
self::assertNull(Election::setMaxParseIteration(null));
self::assertSame([0=>'candidate5', 1=>'candidate6', 2=>'candidate7'], $this->election2->parseCandidates('candidate5;candidate6;candidate7'));
self::assertSame(2, Election::setMaxParseIteration(2));
$this->election2->parseCandidates('candidate8;candidate9;candidate10');
}
/**
* @preserveGlobalState disabled
* @backupStaticAttributes disabled
* @runInSeparateProcess
*/
public function testMaxVoteNumber(): never
{
$this->expectException(VoteMaxNumberReachedException::class);
$this->expectExceptionMessage('The maximal number of votes for the method is reached');
$election = new Election;
self::assertCount(3, $election->parseCandidates('candidate1;candidate2;candidate3'));
self::assertSame(42, Election::setMaxVoteNumber(42));
self::assertSame(21, $election->parseVotes('candidate1>candidate2 * 21'));
try {
$election->parseVotes('candidate1>candidate2 * 42');
self::assertTrue(false);
} catch (VoteMaxNumberReachedException $e) {
$this->assertEquals('The maximal number of votes for the method is reached', $e->getMessage());
}
self::assertSame(21, $election->countVotes());
$election->parseVotes('candidate1 * 21');
self::assertSame(42, $election->countVotes());
self::assertNull(Election::setMaxVoteNumber(null));
$election->addVote('candidate3');
self::assertSame(42, Election::setMaxVoteNumber(42));
try {
$election->addVote('candidate3');
} catch (VoteMaxNumberReachedException $e) {
$reserveException = $e;
}
self::assertNull(Election::setMaxVoteNumber(null));
throw $reserveException;
}
public function testGetVotesListAsString(): void
{
$this->election1 = new Election;
$this->election1->addCandidate('C');
$this->election1->addCandidate('B');
$this->election1->addCandidate('D');
$this->election1->addCandidate('E');
$this->election1->addCandidate('A');
$this->election1->parseVotes('
D * 6
A * 6
E = A = B *3
A > C = B > E * 5
Y > Z
');
self::assertSame(
"A > B = C = D = E * 6\n".
"D > A = B = C = E * 6\n".
"A > B = C > E > D * 5\n".
"A = B = E > C = D * 3\n".
'A = B = C = D = E * 1',
$this->election1->getVotesListAsString()
);
$this->election1->setImplicitRanking(false);
self::assertSame(
"A * 6\n".
"D * 6\n".
"A > B = C > E * 5\n".
"A = B = E * 3\n".
'/EMPTY_RANKING/ * 1',
$this->election1->getVotesListAsString()
);
self::assertSame(
<<<'VOTES'
A * 6
D * 6
A > B = C > E * 5
A = B = E * 3
Y > Z * 1
VOTES,
$this->election1->getVotesListAsString(false)
);
}
public function testEmptyRankingExport(): void
{
$this->election2->parseCandidates('A;B;C');
$this->election2->setImplicitRanking(false);
$this->election2->addVote(new Vote(''));
$this->election2->addVote(new Vote('D>E'));
self::assertSame('/EMPTY_RANKING/ * 2', $this->election2->getVotesListAsString(true));
self::assertSame('/EMPTY_RANKING/ * 1'. "\n" .'D > E * 1', $this->election2->getVotesListAsString(false));
self::assertSame(
$cvotes_explicit_without_context =
<<<'CVOTES'
#/Candidates: A ; B ; C
#/Implicit Ranking: false
#/Weight Allowed: false
/EMPTY_RANKING/ * 1
D > E * 1
CVOTES,
CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: true, inContext: false)
);
self::assertSame(
str_replace(' * 1', '', $cvotes_explicit_without_context),
CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: false, inContext: false)
);
self::assertSame(
<<<'CVOTES'
#/Candidates: A ; B ; C
#/Implicit Ranking: false
#/Weight Allowed: false
/EMPTY_RANKING/ * 2
CVOTES,
CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: true, inContext: true)
);
self::assertSame(
<<<'CVOTES'
#/Candidates: A ; B ; C
#/Implicit Ranking: false
#/Weight Allowed: false
/EMPTY_RANKING/
/EMPTY_RANKING/
CVOTES,
CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: false, inContext: true)
);
$this->election2->setImplicitRanking(true);
self::assertSame(
<<<'CVOTES'
#/Candidates: A ; B ; C
#/Implicit Ranking: true
#/Weight Allowed: false
A = B = C * 2
CVOTES,
CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: true, inContext: true)
);
self::assertSame(
<<<'CVOTES'
#/Candidates: A ; B ; C
#/Implicit Ranking: true
#/Weight Allowed: false
A = B = C
A = B = C
CVOTES,
CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: false, inContext: true)
);
$this->election2 = new Election;
$this->election2->parseCandidates('A;B;C;D');
$this->election2->setImplicitRanking(true);
$this->election2->addVote(new Vote('A>B'));
self::assertSame(
<<<'CVOTES'
#/Candidates: A ; B ; C ; D
#/Implicit Ranking: true
#/Weight Allowed: false
A > B > C = D * 1
CVOTES,
CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: true, inContext: true)
);
self::assertSame(
$cvotes_implicit_without_context =
<<<'CVOTES'
#/Candidates: A ; B ; C ; D
#/Implicit Ranking: true
#/Weight Allowed: false
A > B * 1
CVOTES,
CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: true, inContext: false)
);
self::assertSame(
str_replace(' * 1', '', $cvotes_implicit_without_context),
CondorcetElectionFormat::exportElectionToCondorcetElectionFormat(election: $this->election2, includeNumberOfSeats: false, aggregateVotes: false, inContext: false)
);
}
public function testParseVoteCandidateCoherence(): void
{
$this->election1 = new Election;
$cA = $this->election1->addCandidate('A');
$cB = $this->election1->addCandidate('B');
$cC = $this->election1->addCandidate('C');
self::assertSame(2, $this->election1->parseVotes('
A>B>C * 2
'));
$votes = $this->election1->getVotesList();
foreach ($votes as $vote) {
$ranking = $vote->getRanking();
self::assertSame($cA, $ranking[1][0]);
self::assertSame($cB, $ranking[2][0]);
self::assertSame($cC, $ranking[3][0]);
}
}
public function testParseVotesInvalidPath(): void
{
$this->expectException(FileDoesNotExistException::class);
$this->expectExceptionMessageMatches('/bad_file.txt$/');
$this->election1 = new Election;
$this->election1->addCandidate('A');
$this->election1->addCandidate('B');
$this->election1->parseVotes('bad_file.txt', true);
}
public function testParseVotesWithoutFail(): void
{
$this->election1 = new Election;
$this->election1->addCandidate('A');
$this->election1->addCandidate('B');
$this->election1->addCandidate('C');
self::assertSame(2, $this->election1->parseVotesWithoutFail('
A > B > C
A > B > C * 4;tag1 || A > B > C*4 #Coucou
A < B < C * 10
D <> B
A > B > C
'));
self::assertSame(10, $this->election1->countVotes());
self::assertSame(2, $this->election1->parseVotesWithoutFail(__DIR__.'/../LargeElectionData/smallVote1.votes', true));
self::assertSame(20, $this->election1->countVotes());
self::assertSame(2, $this->election1->parseVotesWithoutFail(new \SplFileObject(__DIR__.'/../LargeElectionData/smallVote1.votes'), true));
self::assertSame(30, $this->election1->countVotes());
self::assertSame(2, $this->election1->parseVotesWithoutFail(new \SplFileInfo(__DIR__.'/../LargeElectionData/smallVote1.votes'), true));
self::assertSame(40, $this->election1->countVotes());
}
public function testParseVotesWithoutFailInvalidPath(): void
{
$this->expectException(FileDoesNotExistException::class);
$this->expectExceptionMessageMatches('/bad_file.txt$/');
$this->election1 = new Election;
$this->election1->addCandidate('A');
$this->election1->addCandidate('B');
$this->election1->parseVotesWithoutFail('bad_file.txt', true);
}
public function testVoteWeight(): void
{
$election = new Election;
$election->addCandidate('A');
$election->addCandidate('B');
$election->addCandidate('C');
$election->addCandidate('D');
$election->parseVotes('
A > C > D * 6
B > A > D * 1
C > B > D * 3
D > B > A * 3
');
$voteWithWeight = $election->addVote('D > C > B');
$voteWithWeight->setWeight(2);
self::assertSame(
14,
$election->sumVotesWeight()
);
self::assertSame(
'D > C > B ^2',
(string) $voteWithWeight
);
self::assertSame(
'D > C > B > A',
$voteWithWeight->getSimpleRanking($election)
);
self::assertNotSame(
'A = D > C > B',
$election->getResult('Schulze Winning')->getResultAsString()
);
$election->allowsVoteWeight(true);
self::assertSame(
15,
$election->sumVotesWeight()
);
self::assertSame(
'D > C > B > A ^2',
$voteWithWeight->getSimpleRanking($election)
);
self::assertSame(
'A = D > C > B',
$election->getResult('Schulze Winning')->getResultAsString()
);
$election->allowsVoteWeight(false);
self::assertSame(
14,
$election->sumVotesWeight()
);
self::assertNotSame(
'A = D > C > B',
$election->getResult('Schulze Winning')->getResultAsString()
);
$election->allowsVoteWeight(!$election->isVoteWeightAllowed());
$election->removeVote($voteWithWeight);
self::assertSame(
13,
$election->sumVotesWeight()
);
$election->parseVotes('
D > C > B ^2 * 1
');
self::assertSame(
15,
$election->sumVotesWeight()
);
self::assertSame(
'A = D > C > B',
$election->getResult('Schulze Winning')->getResultAsString()
);
$election->addVote('D > C > B');
self::assertSame(
<<<'VOTES'
A > C > D > B * 6
C > B > D > A * 3
D > B > A > C * 3
D > C > B > A ^2 * 1
B > A > D > C * 1
D > C > B > A * 1
VOTES,
$election->getVotesListAsString()
);
}
public function testaddVotesFromJson(): never
{
$this->expectException(\JsonException::class);
$election = new Election;
$election->addCandidate('A');
$election->addCandidate('B');
$election->addCandidate('C');
$votes = [];
$votes[]['vote'] = 'B>C>A';
$votes[]['vote'] = new \stdClass; // Invalid Vote
$votes[]['vote'] = ['C', 'B', 'A'];
self::assertSame(2, $election->addVotesFromJson(json_encode($votes)));
self::assertSame(
<<<'VOTES'
B > C > A * 1
C > B > A * 1
VOTES,
$election->getVotesListAsString()
);
$votes = [];
$votes[0]['vote'] = 'A>B>C';
$votes[0]['multi'] = 5;
$votes[0]['tag'] = 'tag1';
$votes[0]['weight'] = '42';
$election->addVotesFromJson(json_encode($votes));
$election->allowsVoteWeight(true);
self::assertSame(
<<<'VOTES'
A > B > C ^42 * 5
B > C > A * 1
C > B > A * 1
VOTES,
$election->getVotesListAsString()
);
self::assertSame(5, $election->countVotes('tag1'));
$election->addVotesFromJson(json_encode($votes).'{42');
}
public function testaddCandidatesFromJson(): never
{
$this->expectException(CandidateExistsException::class);
$this->expectExceptionMessage('This candidate already exists: candidate2');
$election = new Election;
$candidates = ['candidate1 ', 'candidate2'];
$election->addCandidatesFromJson(json_encode($candidates));
self::assertSame(2, $election->countCandidates());
self::assertEquals(['candidate1', 'candidate2'], $election->getCandidatesListAsString());
$election->addCandidatesFromJson(json_encode(['candidate2']));
}
public function testaddCandidatesFromInvalidJson(): never
{
$this->expectException(\JsonException::class);
$election = new Election;
$election->addCandidatesFromJson(json_encode(['candidate3']).'{42');
}
public function testaddVotesFromJsonWithInvalidJson(): void
{
$errors = ['42', 'true', 'false', 'null', '', ' ', json_encode(new \stdClass)];
foreach ($errors as $oneError) {
try {
$this->election2->addVotesFromJson($oneError);
// Else fail
$this->fail("{$oneError} is not a valid Json for PHP");
} catch (\JsonException) {
}
}
self::assertEmpty($this->election2->getVotesList());
}
public function testCachingResult(): void
{
$election = new Election;
$election->addCandidate('A');
$election->addCandidate('B');
$election->addCandidate('C');
$election->addCandidate('D');
$election->addVote($vote1 = new Vote('A > C > D'));
$result1 = $election->getResult('Schulze');
self::assertSame($result1, $election->getResult('Schulze'));
}
public function testElectionSerializing(): void
{
$election = new Election;
$election->addCandidate('A');
$election->addCandidate('B');
$election->addCandidate('C');
$election->addCandidate('D');
$election->addVote($vote1 = new Vote('A > C > D'));
$result1 = $election->getResult('Schulze');
$election = serialize($election);
// file_put_contents("Tests/src/ElectionData/serialized_election_v3.2.0.txt", $election); # For next test
$election = unserialize($election);
self::assertNotSame($result1, $election->getResult('Schulze'));
self::assertSame($result1->getResultAsString(), $election->getResult('Schulze')->getResultAsString());
self::assertNotSame($vote1, $election->getVotesList()[0]);
self::assertSame($vote1->getSimpleRanking(), $election->getVotesList()[0]->getSimpleRanking());
self::assertTrue($election->getVotesList()[0]->haveLink($election));
self::assertFalse($vote1->haveLink($election));
}
public function testElectionUnserializing(): void
{
$this->expectException(ElectionObjectVersionMismatchException::class);
$this->expectExceptionMessage(
"Version mismatch: The election object has version '3.2' " .
"which is different from the current class version '".Condorcet::getVersion(true)."'"
);
unserialize(
file_get_contents('Tests/src/ElectionData/serialized_election_v3.2.0.txt')
);
}
public function testCloneElection(): void
{
$this->election1->computeResult();
$cloneElection = clone $this->election1;
self::assertNotSame($this->election1->getVotesManager(), $cloneElection->getVotesManager());
self::assertSame($this->election1->getVotesList(), $cloneElection->getVotesList());
self::assertSame($this->election1->getCandidatesList(), $cloneElection->getCandidatesList());
self::assertNotSame($this->election1->getPairwise(), $cloneElection->getPairwise());
self::assertEquals($this->election1->getExplicitPairwise(), $cloneElection->getExplicitPairwise());
self::assertNotSame($this->election1->getTimerManager(), $cloneElection->getTimerManager());
self::assertSame($this->election1->getVotesList()[0], $cloneElection->getVotesList()[0]);
self::assertTrue($cloneElection->getVotesList()[0]->haveLink($this->election1));
self::assertTrue($cloneElection->getVotesList()[0]->haveLink($cloneElection));
}
public function testPairwiseArrayAccess(): void
{
$this->election1->computeResult();
self::assertTrue($this->election1->getPairwise()->offsetExists(1));
}
public function testGetCandidateObjectFromKey(): void
{
self::assertSame($this->candidate2, $this->election1->getCandidateObjectFromKey(1));
self::assertNull($this->election1->getCandidateObjectFromKey(42));
}
public function testElectionState1(): never
{
$this->expectException(VotingHasStartedException::class);
$this->expectExceptionMessage("The voting has started: cannot add 'candidate4'");
$this->election1->addCandidate('candidate4');
}
public function testElectionState2(): never
{
$this->expectException(VotingHasStartedException::class);
$this->expectExceptionMessage('The voting has started');
$this->election1->removeCandidates('candidate4');
}
public function testElectionState3(): never
{
$this->expectException(NoCandidatesException::class);
$this->expectExceptionMessage('You need to specify one or more candidates before voting');
$election = new Election;
$election->setStateTovote();
}
public function testElectionState4(): never
{
$this->expectException(ResultRequestedWithoutVotesException::class);
$this->expectExceptionMessage('The result cannot be requested without votes');
$election = new Election;
$election->getResult();
}
public function testElectionState5(): void
{
$this->election1->getResult();
self::assertTrue($this->election1->setStateTovote());
self::assertSame(ElectionState::VOTES_REGISTRATION, $this->election1->getState());
}
public function testAddSameVote(): never
{
$this->expectException(VoteException::class);
$this->expectExceptionMessage('Problem handling vote: seats are already registered');
$this->election1->addVote($this->vote1);
}
public function testDestroy(): void
{
$election = new Election;
$election->addCandidate('candidate1');
$election->addCandidate('candidate2');
$election->addCandidate('candidate3');
$election->addVote('candidate1>candidate2');
$weakref = \WeakReference::create($election);
// PHP circular reference can bug
// \debug_zval_dump($election);
unset($election);
self::assertNull($weakref->get());
}
public function testRemoveCandidate(): never
{
$this->expectException(CandidateDoesNotExistException::class);
$this->expectExceptionMessage('This candidate does not exist: B');
$election = new Election;
$election->addCandidate('candidate1');
$badCandidate = new Candidate('B');
$election->removeCandidates($badCandidate);
}
/**
* @dataProvider MethodsListProvider
*/
public function testRemoveCandidateResult(string $method): void
{
$votes = ' Memphis * 4
Nashville * 3
Chattanooga * 2
Knoxville * 1';
// Ref
$electionRef = new Election;
$electionRef->addCandidate('Memphis');
$electionRef->addCandidate('Nashville');
$electionRef->addCandidate('Knoxville');
$electionRef->addCandidate('Chattanooga');
$electionRef->parseVotes($votes);
// Test
$electionTest = new Election;
$electionTest->addCandidate('Memphis');
$electionTest->addCandidate('BadCandidate');
$electionTest->addCandidate('Nashville');
$electionTest->addCandidate('Knoxville');
$electionTest->addCandidate('Chattanooga');
$electionTest->removeCandidates('BadCandidate');
$electionTest->parseVotes($votes);
self::assertSame(
$electionRef->getResult($method)->getResultAsArray(true),
$electionTest->getResult($method)->getResultAsArray(true)
);
}
public function MethodsListProvider(): array
{
$r = [];
foreach (Condorcet::getAuthMethods() as $method) {
$r[] = [$method];
}
return $r;
}
public function testAmbiguousCandidatesOnElectionSide(): never
{
$this->expectException(VoteInvalidFormatException::class);
$this->expectExceptionMessage('The format of the vote is invalid');
$vote = new Vote('candidate1>candidate2');
$election1 = new Election;
$election2 = new Election;
$election1->addCandidate(new Candidate('candidate2'));
$election2->addCandidate(new Candidate('candidate1'));
$election1->addVote($vote);
$election2->addVote($vote);
}
public function testAmbiguousCandidatesOnVoteSide(): never
{
$this->expectException(VoteInvalidFormatException::class);
$this->expectExceptionMessage('The format of the vote is invalid: vote does not match candidate in this election');
$election1 = new Election;
$election2 = new Election;
$election1->addCandidate(new Candidate('candidate1'));
$election2->addCandidate(new Candidate('candidate2'));
$candidate3 = new Candidate('candidate3');
$election1->addCandidate($candidate3);
$election2->addCandidate($candidate3);
$vote = new Vote('candidate3');
$election1->addVote($vote);
$election2->addVote($vote);
$election1->getResult();
$election2->getResult();
try {
$vote->setRanking('candidate1>candidate2>candidate3');
} catch (\Exception $e) {
}
self::assertEmpty($election1->debugGetCalculator());
self::assertEmpty($election2->debugGetCalculator());
throw $e;
}
public function testInvalidSeats(): never
{
$this->expectException(NoSeatsException::class);
$this->expectExceptionMessage('No seats defined');
$this->election1->setNumberOfSeats(0);
}
public function testSeats(): void
{
self::assertSame(100, $this->election1->getNumberOfSeats());
$this->election1->setNumberOfSeats(5);
self::assertSame(5, $this->election1->getNumberOfSeats());
}
}