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); } }