def testDefeatBatch(self): "tag is name of rule" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b), dict(rule='wigm-prf')) self.assertEqual(E.rule.tag(), 'wigm-prf') E = Election(ElectionProfile(data=b), dict(rule='wigm-prf-batch')) self.assertEqual(E.rule.tag(), 'wigm-prf-batch')
def testDroopOptions(self): "test [droop ...]" if 'meek' in droop.electionRuleNames(): b = '''3 2 [droop arithmetic=fixed precision=4] 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b), dict(rule='meek')) self.assertEqual(E.V.precision, 4) E = Election(ElectionProfile(data=b), dict(rule='meek', precision=6)) self.assertEqual(E.V.precision, 6) b = '''3 2 [droop rational precision=4] 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b), dict(rule='meek')) self.assertEqual(E.V.name, 'rational') b = '''3 2 [droop meek] 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b), dict()) self.assertEqual(E.rule.method, 'meek') E = Election(ElectionProfile(data=b), None) self.assertEqual(E.rule.method, 'meek') b = '''3 2 [droop dump meek] 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b), None) self.assertTrue(E.options.getopt('dump')) b = '''3 2 [droop dump=true meek] 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b), None) self.assertTrue(E.options.getopt('dump')) b = '''3 2 [droop dump=false meek] 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b), None) self.assertFalse(E.options.getopt('dump')) # fake a path to test double-path logic b = '''3 2 [droop 42.blt 513.blt meek] 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' self.assertRaises(UsageError, Election, ElectionProfile(data=b), dict())
def testNickReport(self): "using nicknames shouldn't alter dump or report" b1 = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' b2 = '''3 2 [nick a b c] 4 a b 0 2 c 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b1), dict(rule='meek-prf')) E.count() r1 = E.report() d1 = E.dump() E = Election(ElectionProfile(data=b2), dict(rule='meek-prf')) E.count() r2 = E.report() d2 = E.dump() self.assertEqual(r1, r2) self.assertEqual(d1, d2)
def testMeekWarren1(self): "meek responds to warren" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' profile = ElectionProfile(data=b) E = Election(profile, dict(rule='warren')) self.assertEqual(E.rule.tag(), 'warren-o9') self.assertRaises(UsageError, Election, profile, dict(rule='warren', defeat_batch='whatever'))
def testArithmetic(self): "wigm-prf uses fixed" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b), dict(rule='wigm-prf', arithmetic='guarded', abc=4)) self.assertEqual(E.options.getopt('arithmetic'), 'fixed') self.assertEqual(E.options.getopt('precision'), 4) self.assertEqual(E.options.overrides(), ['arithmetic']) self.assertEqual(E.options.unused(), ['abc'])
def testDefeatRemaining(self): "count a profile that has hopeful candidates left over to defeat" b = '''3 2 4 1 0 4 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "test defeat-remaining"''' E = Election(ElectionProfile(data=b), dict(rule='wigm-prf')) E.count() elected = [c.name for c in E.elected] self.assertEqual(elected, ['Castor', 'Pollux']) defeated = [c.name for c in E.defeated] self.assertEqual(defeated, ['Helen']) report = E.report() self.assertTrue(report.find('Defeat remaining'))
def testElectAll(self): "count a profile with nSeats candidates" b = '''2 2 4 1 0 4 2 0 2 1 0 0 "Castor" "Pollux" "test nseats candidates"''' E = Election(ElectionProfile(data=b), dict(rule='cfer')) E.count() elected = [c.name for c in E.elected] self.assertEqual(elected, ['Castor', 'Pollux']) defeated = [c.name for c in E.defeated] self.assertEqual(defeated, []) report = E.report() self.assertTrue(report.find('Elect all'))
def testDefeatBatch(self): "count a profile that has with defeat_batch option" b = '''4 2 4 1 0 3 2 0 2 3 2 0 0 "Castor" "Pollux" "Helen" "George" "test defeat-batch"''' E = Election(ElectionProfile(data=b), dict(rule='wigm', defeat_batch='zero')) E.count() elected = [c.name for c in E.elected] self.assertEqual(elected, ['Castor', 'Pollux']) defeated = [c.name for c in E.defeated] self.assertEqual(defeated, ['Helen', 'George']) report = E.report() self.assertTrue(report.find('Defeat batch'))
def testReports(self): "look at election outputs" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' profile = ElectionProfile(data=b) rulename = droop.electionRuleNames()[ 0] # pick the first rule arbitrarily E = Election(profile, dict(rule=rulename)) E.count() self.assertEqual(E.report().find('interrupted'), -1) self.assertTrue(E.report(intr=True).find('interrupted') > 0) E = Election(profile, dict(rule=rulename)) E.count() self.assertEqual(E.dump().find('interrupted'), -1) self.assertTrue(E.dump(intr=True).find('interrupted') > 0) E = Election(profile, dict(rule=rulename)) E.count() self.assertEqual(E.json().find('interrupted'), -1) self.assertTrue(E.json(intr=True).find('interrupted') > 0) r = E.record() self.assertTrue(r, dict) self.assertEqual(r['actions'][-1]['tag'], 'log')
def testRules(self): "basic test of each rule" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' profile = ElectionProfile(data=b) for rulename in droop.electionRuleNames(): options = dict(rule=rulename) E = Election(profile, options) self.assertTrue(E.rule.__class__.__name__ == 'Rule', 'bad rule class') self.assertTrue( len(options) >= 1, 'rule should set/leave at least one option') self.assertTrue( E.options.getopt('arithmetic') in ('fixed', 'integer', 'guarded', 'rational'), 'legal arithmetic') candidates = E.C self.assertTrue("Castor" in [c.name for c in candidates]) self.assertTrue("Castor" in [str(c) for c in candidates]) self.assertTrue(1 in [c for c in candidates]) for c in candidates: self.assertEqual(c.order, c.tieOrder) E.count() self.assertEqual(len(E.elected), E.nSeats)
def doDumpCompare(options, filename, subdir=''): ''' helper: do a count and compare dump/report to reference ''' if not filename.endswith('.blt'): filename += '.blt' base, ext = os.path.splitext(filename) # pylint: disable=W0612 blt = os.path.join(testdir, 'blt', subdir, filename) E = Election(ElectionProfile(blt), options) E.count() tag = '%s-%s-%s' % (base, E.rule.tag(), E.V.tag()) def readFile(path): "read a json/dump/report file" f = open(path, 'r') data = f.read() f.close() return data def writeFile(path, data): "write a json/dump/report file" if not os.path.isdir(os.path.dirname(path)): os.makedirs(os.path.dirname(path)) f = open(path, 'w') f.write(data) f.close() # first do report # rref = os.path.join(testdir, 'ref', 'report', subdir, '%s.txt' % tag) rout = os.path.join(testdir, 'out', 'report', subdir, '%s.txt' % tag) report = E.report() if not os.path.isfile(rref): writeFile(rref, report) reportref = readFile(rref) if os.path.isfile(rout): os.unlink(rout) # don't include version number in comparison report0 = re.sub(r'droop v\d+\.\d+', 'droop v0.0', report) reportref = re.sub(r'droop v\d+\.\d+', 'droop v0.0', reportref) if report0 != reportref: writeFile(rout, report) if compare_report: return False # same logic with json # sref = os.path.join(testdir, 'ref', 'json', subdir, '%s.txt' % tag) sout = os.path.join(testdir, 'out', 'json', subdir, '%s.txt' % tag) json = E.json() if not os.path.isfile(sref): writeFile(sref, json) jsonref = readFile(sref) if os.path.isfile(sout): os.unlink(sout) # don't include version number in comparison json0 = re.sub(r'"droop_version": "\d+\.\d+"', '"droop_version": "0.0"', json) jsonref = re.sub(r'"droop_version": "\d+\.\d+"', '"droop_version": "0.0"', jsonref) if json0 != jsonref: writeFile(sout, json) if compare_json: return False # same logic with dump # dref = os.path.join(testdir, 'ref', 'dump', subdir, '%s.txt' % tag) dout = os.path.join(testdir, 'out', 'dump', subdir, '%s.txt' % tag) dump = E.dump() if not os.path.isfile(dref): writeFile(dref, dump) dumpref = readFile(dref) if os.path.isfile(dout): os.unlink(dout) if dump != dumpref: writeFile(dout, dump) if compare_dump: return False return True
def getDump(options, base): "run a count and return the dump" blt = '%s/blt/%s.blt' % (testdir, base) E = Election(ElectionProfile(blt), options) E.count() return E.dump()
def main(options=None): "run an election" if not options: raise droop.common.UsageError("no ballot file specified") # process options # # we know about (path, profile) # all the others are passed to the various consumers # path = None # ballot path must be specified doProfile = False # performance profiling reps = 1 # repetitions (for profiling) for opt, arg in options.items(): if opt == 'path': # path=<path to ballot file> path = arg elif opt == 'profile': # profile=<number of repetitions> import cProfile import pstats reps = int(arg) doProfile = True profilefile = "profile.out" if not path: raise droop.common.UsageError("no ballot file specfied") # run the election # # fetch the election profile # create the Election object # count # report # def countElection(E, repeat=1): "encapsulate for optional profiling" for i in xrange(repeat): # pylint: disable=W0612 E.count() electionProfile = ElectionProfile( path=path) # don't repeat the profile loading E = Election(electionProfile, options) try: intr = False if doProfile: cProfile.runctx('countElection(E, reps)', globals(), locals(), profilefile) else: countElection(E, reps) except KeyboardInterrupt: intr = True E.options.setopt('dump', default=False) E.options.setopt('json', default=False) ereport = '' if E.options.setopt('report', default=True): ereport += E.report(intr) if E.options.getopt('dump'): ereport += E.dump(intr) if E.options.getopt('json'): ereport += E.json(intr) if doProfile: p = pstats.Stats(profilefile) p.strip_dirs().sort_stats('time').print_stats(50) return ereport
def doCount(options, blt): "run the count and return the Election" p = ElectionProfile(testdir + '/blt/' + blt) E = Election(p, options) E.count() return E
def testElectionMpls1(self): "mpls: everyone elected at first" p_mpls1 = '''3 2 4 1 2 0 4 2 1 0 1 3 0 0 "a" "b" "c" "2 elected at first"''' E = Election(ElectionProfile(data=p_mpls1), dict(rule='mpls')) E.count() self.assertEqual(len(E.elected), 2)
def setUp(self): "initialize profile and rule" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' self.Profile = ElectionProfile(data=b) self.E = Election(self.Profile, dict(rule='qpq'))
def doQpqCount(filename): "each of five elections from the Woodall paper" blt = os.path.join(testdir, 'blt', 'qpq', filename) E = Election(ElectionProfile(blt), dict(rule='qpq')) E.count() return E
def testElectionQpq1(self): "qpq: everyone elected at first" b = '''3 2 4 1 2 0 4 2 1 0 1 3 0 0 "a" "b" "c" "2 elected at first"''' E = Election(ElectionProfile(data=b), dict(rule='qpq')) E.count() self.assertEqual(len(E.elected), 2)