Some love for the interface & tests

This commit is contained in:
Petr Viktorin 2011-04-27 16:00:50 +03:00
parent b27187e99a
commit 11d803fd63
2 changed files with 121 additions and 80 deletions

View file

@ -1,62 +1,82 @@
import sys
from pokedex.db import connect
from pokedex.util.movesets import main
testcase_args = u"""
NO muk
NO beedrill rage pursuit agility endeavor toxic
NO ditto psystrike aeroblast mist-ball judgment
OK metapod tackle harden
OK lugia aeroblast punishment dive snore
OK yanmega bug-bite bug-buzz tackle whirlwind
OK crobat brave-bird quick-attack gust zen-headbutt
OK bagon defense-curl fire-fang hydro-pump shadow-claw
OK volcarona endure toxic fly fire-blast
OK hippopotas curse revenge sleep-talk swallow
OK hippopotas curse revenge sleep-talk snore
OK smeargle bug-bite bug-buzz splash fire-blast
NO smeargle bug-bite chatter splash fire-blast
NO azurill muddy-water iron-tail scald mimic
OK salamence dragon-dance dragon-claw fire-blast earthquake -v platinum
OK crawdaunt brick-break rock-slide facade toxic -v platinum
NO cleffa tickle wish amnesia splash
OK tyrogue pursuit
NO happiny softboiled
NO mamoswine bite body-slam curse double-edge
OK shedinja swords-dance
NO shedinja swords-dance screech
OK shedinja screech double-team fury-cutter x-scissor
OK raichu volt-tackle
OK raichu surf -v gold
OK pikachu volt-tackle thunderbolt bide
OK gyarados flail thrash iron-head outrage
OK drifblim memento gust thunderbolt pain-split
OK crobat nasty-plot brave-bird
NO crobat nasty-plot hypnosis
OK garchomp double-edge thrash outrage
OK nidoking counter disable amnesia head-smash
OK aggron stomp smellingsalt screech fire-punch
OK tyranitar dragon-dance outrage thunder-wave surf
NO butterfree morning-sun harden
OK pikachu reversal bide nasty-plot discharge
NO pikachu surf charge
NO blissey wish counter
NO clefairy copycat dynamicpunch
"""
result_map = {'OK': True, 'NO': False}
def test_cases():
session = connect()
for argstring in u"""
NO muk
NO beedrill rage pursuit agility endeavor toxic
NO ditto psystrike aeroblast mist-ball judgment
OK lugia aeroblast punishment dive snore
OK yanmega bug-bite bug-buzz tackle whirlwind
OK crobat brave-bird quick-attack gust zen-headbutt
OK bagon defense-curl fire-fang hydro-pump shadow-claw
OK volcarona endure toxic fly fire-blast
OK hippopotas curse revenge sleep-talk swallow
OK hippopotas curse revenge sleep-talk snore
OK smeargle bug-bite bug-buzz splash fire-blast
NO smeargle bug-bite chatter splash fire-blast
NO azurill muddy-water iron-tail scald mimic
OK salamence dragon-dance dragon-claw fire-blast earthquake -v platinum
OK crawdaunt brick-break rock-slide facade toxic -v platinum
NO cleffa tickle wish amnesia splash
OK tyrogue pursuit
NO happiny softboiled
NO mamoswine bite body-slam curse double-edge
OK raichu volt-tackle
OK raichu surf -v gold
OK pikachu volt-tackle thunderbolt bide
OK gyarados flail thrash iron-head outrage
OK drifblim memento gust thunderbolt pain-split
OK crobat nasty-plot brave-bird
NO crobat nasty-plot hypnosis
OK garchomp double-edge thrash outrage
OK nidoking counter disable amnesia head-smash
OK aggron stomp smellingsalt screech fire-punch
OK tyranitar dragon-dance outrage thunder-wave surf
NO butterfree morning-sun harden
OK pikachu reversal bide nasty-plot discharge
NO pikachu surf charge
NO blissey wish counter
NO clefairy copycat dynamicpunch
""".strip().splitlines():
for argstring in testcase_args.strip().splitlines():
def run_test(argstring):
args = argstring.split()
assert main(args[1:], session=session) == result_map[args[0]]
args = argstring.split() + ['-q']
assert bool(main(args[1:], session=session)) == result_map[args[0]]
run_test.description = 'Moveset checker test: ' + argstring.strip()
yield run_test, argstring.strip()
if __name__ == '__main__':
# Nose's default profiler, the unmaintained hotshot, sucks.
# Use cProfile instead.
filename = 'movesets.profile'
print 'Profiling the moveset checker'
import cProfile
def header(str):
print
print str
cProfile.runctx("[(header(argv), f(argv)) for f, argv in test_cases()]",
ok_fail = [0, 0]
def run_case(f, argv):
print argv, '...',
sys.stdout.flush()
try:
f(argv)
ok_fail[0] += 1
print 'ok'
except AssertionError:
ok_fail[1] += 1
print 'FAIL'
cases = list(test_cases())
cProfile.runctx("[(run_case(f, argv)) for f, argv in cases]",
globals(), locals(), filename=filename)
print "{0} tests: {1[0]} OK, {1[1]} failed".format(sum(ok_fail), ok_fail)
print 'Profile stats saved to', filename

View file

@ -45,7 +45,7 @@ class MovesetSearch(object):
_cache = WeakKeyDictionary()
def __init__(self, session, pokemon, version, moves, level=100, costs=None,
exclude_versions=(), exclude_pokemon=(), debug_level=False):
exclude_versions=(), exclude_pokemon=(), debug_level=0):
self.session = session
@ -1263,9 +1263,23 @@ class GoalNode(PokemonNode):
return True
###
### CLI interface
### Interface
###
def verify_moveset(session, pokemon, version, moves, level=100, **kwargs):
"""Verify the given moveset.
Returns a result with a hint on how to obtain the moveset, if it is valid.
Otherwise, returns a false value.
"""
try:
search = MovesetSearch(session, pokemon, version, moves, level, **kwargs)
except IllegalMoveCombination, e:
return False
else:
for result in search:
return result
def print_result(result, moves=()):
template = u"{cost:4} {est:4} {action:45.45}{long:1} {pokemon:10}{level:>3}{nl:1}{versions:2} {moves}"
print template.format(cost='Cost', est='Est.', action='Action', pokemon='Pokemon',
@ -1304,6 +1318,10 @@ def main(argv, session=None):
default='black',
help='Version to search in.')
parser.add_argument('-q', '--quiet', action='store_true', default=False,
help="Don't print out the result, only indicate it by the return "
"value.")
parser.add_argument('-V', '--exclude-version', metavar='VER', type=unicode,
action='append', default=[],
help='Versions to exclude (along with their '
@ -1316,8 +1334,8 @@ def main(argv, session=None):
parser.add_argument('-d', '--debug', action='append_const', const=1,
default=[],
help='Output timing and debugging information (can be specified more '
'than once).')
help='Output timing and debugging information. Can be specified more '
'than once for even more verbosity.')
args = parser.parse_args(argv)
args.debug = len(args.debug)
@ -1331,54 +1349,57 @@ def main(argv, session=None):
if args.debug:
print 'Parsing arguments'
class BadArgs(ValueError): pass
def _get_list(table, idents, name):
if not idents:
return []
result = []
query = session.query(table).filter(table.identifier.in_(idents))
query = query.order_by(table.id.desc()) # overwrite pokemon alt. forms
ident_map = dict((thing.identifier, thing) for thing in query)
for ident in idents:
try:
result.append(util.get(session, table, identifier=ident))
except NoResultFound:
result.append(ident_map[ident])
except KeyError:
print>>sys.stderr, ('%s %s not found. Please use '
'the identifier.' % (name, ident))
return False
raise BadArgs
return result
pokemon = _get_list(tables.Pokemon, [args.pokemon], 'Pokemon')[0]
try:
all_pokemon = _get_list(tables.Pokemon,
[args.pokemon] + args.exclude_pokemon, 'Pokemon')
all_versions = _get_list(tables.Version,
[args.version] + args.exclude_version, 'Version')
pokemon = all_pokemon[0]
moves = _get_list(tables.Move, args.move, 'Move')
version = _get_list(tables.Version, [args.version], 'Version')[0]
excl_versions = _get_list(tables.Version, args.exclude_version, 'Version')
excl_pokemon = _get_list(tables.Pokemon, args.exclude_pokemon, 'Pokemon')
version = all_versions[0]
excl_versions = all_versions[1:]
excl_pokemon = all_pokemon[1:]
except BadArgs:
return False
if args.debug:
print 'Starting search'
no_results = True
try:
search = MovesetSearch(session, pokemon, version, moves, args.level,
result = verify_moveset(session, pokemon, version, moves, args.level,
exclude_versions=excl_versions, exclude_pokemon=excl_pokemon,
debug_level=args.debug)
except IllegalMoveCombination, e:
print 'Error:', e
# XXX: Support more than one result
if result:
if args.debug:
print '-' * 79
if not args.quiet:
print_result(result, moves=moves)
else:
if args.debug:
print 'Setup done'
for result in search:
if args.debug and search.output_objects:
print '**warning: search looked up output objects**'
no_results = False
print '-' * 79
print_result(result, moves=moves)
# XXX: Support more than one result
break
if args.debug:
print
print 'Done'
if no_results:
print ' ' * 79
if not args.quiet:
print 'Illegal move combination.'
return (not no_results)
return result
if __name__ == '__main__':
sys.exit(not main(sys.argv[1:]))