test adding condorcet vote
This commit is contained in:
@ -0,0 +1,404 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CondorcetPHP\Condorcet\Tests\Console\Commands;
|
||||
|
||||
use CondorcetPHP\Condorcet\Console\Commands\ElectionCommand;
|
||||
use CondorcetPHP\Condorcet\Throwable\{CandidateExistsException, ResultRequestedWithoutVotesException};
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use CondorcetPHP\Condorcet\Console\CondorcetApplication;
|
||||
use CondorcetPHP\Condorcet\Console\Style\CondorcetStyle;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
class ElectionCommandTest extends TestCase
|
||||
{
|
||||
private readonly CommandTester $electionCommand;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
CondorcetApplication::create();
|
||||
|
||||
$this->electionCommand = new CommandTester(CondorcetApplication::$SymfonyConsoleApplication->find('election'));
|
||||
}
|
||||
|
||||
public function testConsoleSimpleElection(): void
|
||||
{
|
||||
$this->electionCommand->execute(
|
||||
[
|
||||
'--candidates' => 'A;B;C',
|
||||
'--votes' => 'A>B>C;C>B>A;B>A>C',
|
||||
'--stats' => null,
|
||||
'--natural-condorcet' => null,
|
||||
'--allows-votes-weight' => null,
|
||||
'--no-tie' => null,
|
||||
'--list-votes' => null,
|
||||
'--deactivate-implicit-ranking' => null,
|
||||
'--show-pairwise' => null,
|
||||
],
|
||||
[
|
||||
'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
|
||||
]
|
||||
);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertStringContainsString('3 candidates registered || 3 votes registered', $output);
|
||||
|
||||
self::assertStringContainsString('Schulze', $output);
|
||||
self::assertStringContainsString('Registered candidates', $output);
|
||||
self::assertStringContainsString('Stats - votes registration', $output);
|
||||
self::assertStringContainsString('Registered Votes List', $output);
|
||||
self::assertStringContainsString('Pairwise', $output);
|
||||
self::assertStringContainsString('Stats:', $output);
|
||||
|
||||
self::assertMatchesRegularExpression('/Is vote weight allowed\?( )+TRUE/', $output);
|
||||
self::assertMatchesRegularExpression('/Votes are evaluated according to the implicit ranking rule\?( )+FALSE./', $output);
|
||||
self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+FALSE/', $output);
|
||||
}
|
||||
|
||||
public function testConsoleSeats(): void
|
||||
{
|
||||
$this->electionCommand->execute(
|
||||
[
|
||||
'--candidates' => 'A;B;C',
|
||||
'--votes' => 'A>B>C;C>B>A;B>A>C',
|
||||
'--stats' => null,
|
||||
'--natural-condorcet' => null,
|
||||
'--allows-votes-weight' => null,
|
||||
'--no-tie' => null,
|
||||
'--list-votes' => null,
|
||||
'--deactivate-implicit-ranking' => null,
|
||||
'--show-pairwise' => null,
|
||||
|
||||
'--seats' => 42,
|
||||
'methods' => ['STV'],
|
||||
],
|
||||
[
|
||||
'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
|
||||
]
|
||||
);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertStringContainsString('3 candidates registered || 3 votes registered', $output);
|
||||
|
||||
self::assertStringContainsString('Seats:', $output);
|
||||
self::assertStringContainsString('42', $output);
|
||||
}
|
||||
|
||||
public function testQuotas(): void
|
||||
{
|
||||
$this->electionCommand->execute([
|
||||
'--candidates' => 'A;B;C',
|
||||
'--votes' => 'A>B>C;C>B>A;B>A>C',
|
||||
|
||||
'methods' => ['STV'],
|
||||
]);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+TRUE/', $output);
|
||||
self::assertStringContainsString('Droop Quota', $output);
|
||||
|
||||
$this->electionCommand->execute([
|
||||
'--candidates' => 'A;B;C',
|
||||
'--votes' => 'A>B>C;C>B>A;B>A>C',
|
||||
|
||||
'methods' => ['STV'],
|
||||
'--quota' => 'imperiali',
|
||||
]);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Imperiali', $output);
|
||||
}
|
||||
|
||||
public function testConsoleAllMethodsArgument(): void
|
||||
{
|
||||
$this->electionCommand->execute([
|
||||
'--candidates' => 'A;B;C',
|
||||
'--votes' => 'A>B>C;C>B>A;B>A>C',
|
||||
|
||||
'methods' => ['all'],
|
||||
]);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
// \var_dump($output);
|
||||
|
||||
self::assertStringContainsString('Copeland', $output);
|
||||
}
|
||||
|
||||
public function testConsoleMultiplesMethods(): void
|
||||
{
|
||||
$this->electionCommand->execute([
|
||||
'--candidates' => 'A;B;C',
|
||||
'--votes' => 'A>B>C;C>B>A;B>A>C',
|
||||
|
||||
'methods' => ['Copeland', 'RankedPairs', 'Minimax'],
|
||||
]);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
// \var_dump($output);
|
||||
|
||||
self::assertStringContainsString('Copeland', $output);
|
||||
self::assertStringContainsString('Ranked Pairs M', $output);
|
||||
self::assertStringContainsString('Minimax Winning', $output);
|
||||
}
|
||||
|
||||
public function testConsoleFileInput(): void
|
||||
{
|
||||
$this->electionCommand->execute([
|
||||
'--candidates' => __DIR__.'/data.candidates',
|
||||
'--votes' => __DIR__.'/data.votes',
|
||||
]);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
// \var_dump($output);
|
||||
|
||||
self::assertStringContainsString('Schulze', $output);
|
||||
self::assertStringContainsString('A,B', $output);
|
||||
self::assertStringContainsString('C '.CondorcetStyle::CONDORCET_LOSER_SYMBOL, $output);
|
||||
}
|
||||
|
||||
public function testInteractiveCommand(): void
|
||||
{
|
||||
$this->electionCommand->setInputs([
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'',
|
||||
'A>B>C',
|
||||
'B>A>C',
|
||||
'A>C>B',
|
||||
'',
|
||||
]);
|
||||
|
||||
$this->electionCommand->execute([
|
||||
'command' => 'election',
|
||||
]);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
// \var_dump($output);
|
||||
|
||||
self::assertStringContainsString('Results: Schulze Winning', $output);
|
||||
}
|
||||
|
||||
public function testNonInteractionMode(): never
|
||||
{
|
||||
$this->expectException(ResultRequestedWithoutVotesException::class);
|
||||
$this->expectExceptionMessage('The result cannot be requested without votes');
|
||||
|
||||
$this->electionCommand->execute([], ['interactive' => false]);
|
||||
|
||||
// $output = $this->electionCommand->getDisplay();
|
||||
// \var_dump($output);
|
||||
}
|
||||
|
||||
public function testCustomizeVotesPerMb(): void
|
||||
{
|
||||
$this->electionCommand->execute([
|
||||
'--candidates' => 'A;B;C',
|
||||
'--votes' => 'A>B>C;C>B>A;B>A>C',
|
||||
'--votes-per-mb' => 42,
|
||||
]);
|
||||
|
||||
self::assertSame(42, \CondorcetPHP\Condorcet\Console\Commands\ElectionCommand::$VotesPerMB);
|
||||
|
||||
// $output = $this->electionCommand->getDisplay();
|
||||
// \var_dump($output);
|
||||
}
|
||||
|
||||
public function testVoteWithDb1(): void
|
||||
{
|
||||
ElectionCommand::$forceIniMemoryLimitTo = '128M';
|
||||
|
||||
$this->electionCommand->execute([
|
||||
'--candidates' => 'A;B;C',
|
||||
'--votes-per-mb' => 1,
|
||||
'--votes' => 'A>B>C * '.(((int) preg_replace('`[^0-9]`', '', ElectionCommand::$forceIniMemoryLimitTo)) + 1), # Must be superior to memory limit in MB
|
||||
], [
|
||||
'verbosity' => OutputInterface::VERBOSITY_DEBUG,
|
||||
]);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertMatchesRegularExpression('/Votes per Mb +1/', $output);
|
||||
self::assertMatchesRegularExpression('/Db is used +yes, using path\\:/', $output);
|
||||
|
||||
ElectionCommand::$forceIniMemoryLimitTo = null;
|
||||
|
||||
# And absence of this error: unlink(path): Resource temporarily unavailable
|
||||
}
|
||||
|
||||
|
||||
public function testNaturalCondorcet(): void
|
||||
{
|
||||
$this->electionCommand->execute([
|
||||
'--candidates' => 'A;B;C',
|
||||
'--votes' => 'A=B=C',
|
||||
'--natural-condorcet' => true,
|
||||
]);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertStringContainsString(CondorcetStyle::CONDORCET_WINNER_SYMBOL.' Condorcet Winner | -', $output);
|
||||
self::assertStringContainsString(CondorcetStyle::CONDORCET_LOSER_SYMBOL.' Condorcet Loser | -', $output);
|
||||
}
|
||||
|
||||
public function testFromCondorcetElectionFormat_DoubleCandidates(): void
|
||||
{
|
||||
$this->expectException(CandidateExistsException::class);
|
||||
|
||||
$this->electionCommand->execute(
|
||||
[
|
||||
'--candidates' => 'A;B;C',
|
||||
'--import-condorcet-election-format' => __DIR__.'/../../Tools/Converters/CondorcetElectionFormatData/test1.cvotes',
|
||||
],
|
||||
[
|
||||
'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function testFromCondorcetElectionFormat_ArgumentpriorityAndDoubleVoteArgument(): void
|
||||
{
|
||||
$this->electionCommand->execute(
|
||||
[
|
||||
'--import-condorcet-election-format' => __DIR__.'/../../Tools/Converters/CondorcetElectionFormatData/test1.cvotes',
|
||||
'--votes' => 'C>A',
|
||||
'--deactivate-implicit-ranking' => null,
|
||||
'--no-tie' => null,
|
||||
'--allows-votes-weight' => null,
|
||||
],
|
||||
[
|
||||
'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
|
||||
]
|
||||
);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertStringContainsString('3 candidates registered || 2 votes registered', $output);
|
||||
|
||||
self::assertStringContainsString('Schulze', $output);
|
||||
self::assertStringContainsString('Registered candidates', $output);
|
||||
self::assertStringContainsString('Stats - votes registration', $output);
|
||||
|
||||
self::assertMatchesRegularExpression('/Is vote weight allowed\?( )+TRUE/', $output);
|
||||
self::assertMatchesRegularExpression('/Votes are evaluated according to the implicit ranking rule\?( )+FALSE./', $output);
|
||||
self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+FALSE/', $output);
|
||||
|
||||
self::assertStringContainsString('Sum vote weight | 3', $output);
|
||||
}
|
||||
|
||||
public function testFromCondorcetElectionFormat_Arguments(): void
|
||||
{
|
||||
$this->electionCommand->execute(
|
||||
[
|
||||
'--import-condorcet-election-format' => __DIR__.'/../../Tools/Converters/CondorcetElectionFormatData/test2.cvotes',
|
||||
],
|
||||
[
|
||||
'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
|
||||
]
|
||||
);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertStringContainsString('3 candidates registered || 2 votes registered', $output);
|
||||
|
||||
self::assertStringContainsString('Schulze', $output);
|
||||
self::assertStringContainsString('Registered candidates', $output);
|
||||
self::assertStringContainsString('Stats - votes registration', $output);
|
||||
|
||||
self::assertMatchesRegularExpression('/Is vote weight allowed\?( )+FALSE/', $output);
|
||||
self::assertMatchesRegularExpression('/Votes are evaluated according to the implicit ranking rule\?( )+FALSE./', $output);
|
||||
self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+TRUE/', $output);
|
||||
|
||||
self::assertStringContainsString('Sum vote weight | 2', $output);
|
||||
|
||||
self::assertStringContainsString('B '.CondorcetStyle::CONDORCET_WINNER_SYMBOL, $output); # Condorcet Winner
|
||||
}
|
||||
|
||||
public function testVoteWithDb_CondorcetElectionFormat(): void
|
||||
{
|
||||
ElectionCommand::$forceIniMemoryLimitTo = '128M';
|
||||
|
||||
$this->electionCommand->execute([
|
||||
'--votes-per-mb' => 1,
|
||||
'--import-condorcet-election-format' => __DIR__.'/../../Tools/Converters/CondorcetElectionFormatData/test3.cvotes',
|
||||
], [
|
||||
'verbosity' => OutputInterface::VERBOSITY_DEBUG,
|
||||
]);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertMatchesRegularExpression('/Votes per Mb +1/', $output);
|
||||
self::assertStringContainsString('Db is used', $output);
|
||||
self::assertStringContainsString('yes, using path:', $output);
|
||||
|
||||
ElectionCommand::$forceIniMemoryLimitTo = null;
|
||||
|
||||
# And absence of this error: unlink(path): Resource temporarily unavailable
|
||||
}
|
||||
|
||||
public function testFromDebianFormat(): void
|
||||
{
|
||||
$this->electionCommand->execute(
|
||||
[
|
||||
'--import-debian-format' => __DIR__.'/../../Tools/Converters/DebianData/leader2020_tally.txt',
|
||||
'methods' => ['STV'],
|
||||
],
|
||||
[
|
||||
'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
|
||||
]
|
||||
);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertStringContainsString('4 candidates registered || 339 votes registered', $output);
|
||||
|
||||
self::assertStringContainsString('STV', $output);
|
||||
self::assertStringContainsString('Registered candidates', $output);
|
||||
self::assertStringContainsString('Stats - votes registration', $output);
|
||||
|
||||
self::assertMatchesRegularExpression('/Is vote weight allowed\?( )+FALSE/', $output);
|
||||
self::assertMatchesRegularExpression('/Votes are evaluated according to the implicit ranking rule\?( )+TRUE./', $output);
|
||||
self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+TRUE/', $output);
|
||||
|
||||
self::assertStringContainsString('Sum vote weight | 339', $output);
|
||||
|
||||
self::assertStringContainsString('Jonathan Carter '.CondorcetStyle::CONDORCET_WINNER_SYMBOL, $output); # Condorcet Winner
|
||||
self::assertMatchesRegularExpression('/Seats: *\| 1/', $output);
|
||||
}
|
||||
|
||||
public function testFromDavidHillFormat(): void
|
||||
{
|
||||
$this->electionCommand->execute(
|
||||
[
|
||||
'--import-david-hill-format' => __DIR__.'/../../Tools/Converters/TidemanData/A1.HIL',
|
||||
'methods' => ['STV'],
|
||||
],
|
||||
[
|
||||
'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
|
||||
]
|
||||
);
|
||||
|
||||
$output = $this->electionCommand->getDisplay();
|
||||
|
||||
self::assertStringContainsString('10 candidates registered || 380 votes registered', $output);
|
||||
|
||||
self::assertStringContainsString('STV', $output);
|
||||
self::assertStringContainsString('Registered candidates', $output);
|
||||
self::assertStringContainsString('Stats - votes registration', $output);
|
||||
|
||||
self::assertMatchesRegularExpression('/Is vote weight allowed\?( )+FALSE/', $output);
|
||||
self::assertMatchesRegularExpression('/Votes are evaluated according to the implicit ranking rule\?( )+TRUE./', $output);
|
||||
self::assertMatchesRegularExpression('/Is vote tie in rank allowed\?( )+TRUE/', $output);
|
||||
|
||||
self::assertStringContainsString('Sum vote weight | 380', $output);
|
||||
|
||||
self::assertStringContainsString('Candidate 1 '.CondorcetStyle::CONDORCET_WINNER_SYMBOL, $output); # Condorcet Winner
|
||||
self::assertMatchesRegularExpression('/Seats: *\| 3/', $output);
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
A
|
||||
B
|
||||
C
|
2
include/Condorcet/Tests/src/Console/Commands/data.votes
Normal file
2
include/Condorcet/Tests/src/Console/Commands/data.votes
Normal file
@ -0,0 +1,2 @@
|
||||
A>B=C
|
||||
B>A>C
|
Reference in New Issue
Block a user